├── .github ├── dependabot.yml └── workflows │ ├── main.yml │ ├── push.yml │ └── stale.yml ├── .gitignore ├── .golangci.yml ├── Dockerfile ├── LICENSES └── Apache-2.0.txt ├── Makefile ├── NOTICE.txt ├── README.md ├── VERSION ├── VERSION.license ├── amf.go ├── amfTest ├── amfcfg.yaml └── amfcfg_with_custom_webui_url.yaml ├── amf_test.go ├── communication ├── api_individual_subscription_document.go ├── api_individual_ue_context_document.go ├── api_n1_n2_individual_subscription_document.go ├── api_n1_n2_message_collection_document.go ├── api_n1_n2_subscriptions_collection_for_individual_ue_contexts_document.go ├── api_non_uen2_message_notification_individual_subscription_document.go ├── api_non_uen2_messages_collection_document.go ├── api_non_uen2_messages_subscriptions_collection_document.go ├── api_subscriptions_collection_document.go └── routers.go ├── consumer ├── am_policy.go ├── communication.go ├── nf_discovery.go ├── nf_mangement.go ├── nsselection.go ├── sm_context.go ├── subscriber_data_management.go ├── ue_authentication.go └── ue_context_management.go ├── context ├── 3gpp_types.go ├── amf_ran.go ├── amf_ue.go ├── common_function.go ├── context.go ├── db.go ├── ran_ue.go ├── sm_context.go ├── timer.go └── transaction.go ├── docs └── images │ ├── README-AMF.png │ └── README-AMF.png.license ├── eventexposure ├── api_individual_subscription_document.go ├── api_subscriptions_collection_document.go └── routers.go ├── factory ├── amf_config_test.go ├── config.go └── factory.go ├── gmm ├── handler.go ├── init.go ├── init_test.go ├── message │ ├── build.go │ └── send.go ├── mock_gmm.go └── sm.go ├── go.mod ├── go.mod.license ├── go.sum ├── go.sum.license ├── httpcallback ├── api_am_policy_control_update_notify.go ├── api_dereg_notify.go ├── api_n1_message_notify.go ├── api_nf_subscribe_notify.go ├── api_sm_context_status_notify.go └── router.go ├── location ├── api_individual_ue_context_document.go └── routers.go ├── logger └── logger.go ├── metrics ├── kafka.go └── telemetry.go ├── msgtypes └── ngapmsgtypes │ └── ngapmsgtypes.go ├── mt ├── api_ue_context_document.go ├── api_ue_reach_ind_document.go └── routers.go ├── nas ├── dispatch.go ├── handler.go └── nas_security │ └── security.go ├── ngap ├── dispatcher.go ├── handler.go ├── message │ ├── build.go │ ├── forward_ie.go │ ├── send.go │ └── send_test.go.test ├── ngap_test.go ├── service │ └── service.go └── util │ └── ngap_util.go ├── oam ├── api_purge_ue_context.go ├── api_registered_ue_context.go └── routers.go ├── producer ├── callback.go ├── callback │ ├── n1n2message.go │ ├── subscription.go │ └── ue_context.go ├── event_exposure.go ├── location_info.go ├── mt.go ├── n1n2message.go ├── oam.go ├── oam_test.go ├── subscription.go └── ue_context.go ├── protos ├── sdcoreAmfServer │ ├── server.pb.go │ └── server_grpc.pb.go └── server.proto ├── service ├── amf_server.go └── init.go └── util ├── convert.go ├── init_context.go ├── json.go ├── mock.drsm.go ├── search_nf_service.go └── test ├── testAmfcfg.yaml └── testAmfcfg2.yaml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # Copyright 2022 Intel Corporation 3 | 4 | version: 2 5 | updates: 6 | 7 | - package-ecosystem: "docker" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | day: "sunday" 12 | time: "21:00" 13 | timezone: "America/Los_Angeles" 14 | 15 | - package-ecosystem: "gomod" 16 | directory: "/" 17 | schedule: 18 | interval: "weekly" 19 | day: "sunday" 20 | time: "21:00" 21 | timezone: "America/Los_Angeles" 22 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # Copyright 2023 Canonical Ltd. 3 | # Copyright 2024 Intel Corporation 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | push: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | build: 14 | uses: omec-project/.github/.github/workflows/build.yml@main 15 | with: 16 | branch_name: ${{ github.ref }} 17 | 18 | docker-build: 19 | uses: omec-project/.github/.github/workflows/docker-build.yml@main 20 | with: 21 | branch_name: ${{ github.ref }} 22 | 23 | static-analysis: 24 | uses: omec-project/.github/.github/workflows/static-analysis.yml@main 25 | with: 26 | branch_name: ${{ github.ref }} 27 | 28 | lint: 29 | uses: omec-project/.github/.github/workflows/lint.yml@main 30 | with: 31 | branch_name: ${{ github.ref }} 32 | 33 | hadolint: 34 | uses: omec-project/.github/.github/workflows/hadolint.yml@main 35 | with: 36 | branch_name: ${{ github.ref }} 37 | 38 | license-check: 39 | uses: omec-project/.github/.github/workflows/license-check.yml@main 40 | with: 41 | branch_name: ${{ github.ref }} 42 | 43 | fossa-scan: 44 | uses: omec-project/.github/.github/workflows/fossa-scan.yml@main 45 | with: 46 | branch_name: ${{ github.ref }} 47 | 48 | unit-tests: 49 | uses: omec-project/.github/.github/workflows/unit-test.yml@main 50 | with: 51 | branch_name: ${{ github.ref }} 52 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # Copyright 2024 Intel Corporation 3 | # Copyright 2025 Canonical Ltd. 4 | on: 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - "VERSION" 10 | 11 | jobs: 12 | tag-github: 13 | uses: omec-project/.github/.github/workflows/tag-github.yml@main 14 | secrets: inherit 15 | 16 | release-image: 17 | needs: tag-github 18 | uses: omec-project/.github/.github/workflows/release-image.yml@main 19 | with: 20 | changed: ${{ needs.tag-github.outputs.changed }} 21 | version: ${{ needs.tag-github.outputs.version }} 22 | secrets: inherit 23 | 24 | update-version: 25 | needs: tag-github 26 | uses: omec-project/.github/.github/workflows/update-version.yml@main 27 | with: 28 | changed: ${{ needs.tag-github.outputs.changed }} 29 | version: ${{ needs.tag-github.outputs.version }} 30 | secrets: inherit 31 | 32 | branch-release: 33 | needs: tag-github 34 | uses: omec-project/.github/.github/workflows/branch-release.yml@main 35 | with: 36 | release_branch: ${{ needs.tag-github.outputs.release_branch }} 37 | version_branch: ${{ needs.tag-github.outputs.version_branch }} 38 | secrets: inherit 39 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # Copyright 2024 Intel Corporation 3 | # Copyright 2025 Canonical Ltd. 4 | on: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | stale: 10 | uses: omec-project/.github/.github/workflows/stale-issue.yml@main 11 | with: 12 | days_before_stale: 120 13 | days_before_close: 15 14 | secrets: inherit 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | # Copyright 2019 free5GC.org 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | # Toolchain 8 | # Goland project folder 9 | .idea/ 10 | # Visual Studio Code 11 | .vscode/ 12 | # emacs/vim 13 | GPATH 14 | GRTAGS 15 | GTAGS 16 | TAGS 17 | tags 18 | cscope.* 19 | # mac 20 | .DS_Store 21 | 22 | # debug 23 | *.log 24 | *.pcap 25 | vendor* 26 | 27 | gmm/gmm.dot 28 | 29 | # coverage 30 | ./coverage 31 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Intel Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | version: "2" 5 | run: 6 | concurrency: 4 7 | issues-exit-code: 1 8 | tests: true 9 | allow-parallel-runners: true 10 | output: 11 | formats: 12 | text: 13 | path: stdout 14 | print-linter-name: true 15 | print-issued-lines: true 16 | linters: 17 | enable: 18 | - asciicheck 19 | - dogsled 20 | - govet 21 | - goconst 22 | - godox 23 | - gomodguard 24 | - misspell 25 | - nakedret 26 | - noctx 27 | - predeclared 28 | - staticcheck 29 | - unconvert 30 | - whitespace 31 | settings: 32 | errcheck: 33 | check-type-assertions: false 34 | check-blank: true 35 | funlen: 36 | lines: 60 37 | statements: 40 38 | gocognit: 39 | min-complexity: 10 40 | goconst: 41 | min-len: 3 42 | min-occurrences: 3 43 | gocritic: 44 | disabled-checks: 45 | - regexpMust 46 | enabled-tags: 47 | - performance 48 | disabled-tags: 49 | - experimental 50 | settings: 51 | captLocal: 52 | paramsOnly: true 53 | rangeValCopy: 54 | sizeThreshold: 32 55 | gocyclo: 56 | min-complexity: 10 57 | godox: 58 | keywords: 59 | - FIXME 60 | - BUG 61 | - XXX 62 | govet: 63 | enable-all: true 64 | disable: 65 | - fieldalignment 66 | settings: 67 | printf: 68 | funcs: 69 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof 70 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf 71 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf 72 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf 73 | lll: 74 | line-length: 120 75 | tab-width: 1 76 | nakedret: 77 | max-func-lines: 30 78 | nestif: 79 | min-complexity: 4 80 | testpackage: 81 | skip-regexp: (export|internal)_test\.go 82 | whitespace: 83 | multi-if: false 84 | multi-func: false 85 | wsl: 86 | strict-append: true 87 | allow-assign-and-call: true 88 | allow-multiline-assign: true 89 | force-case-trailing-whitespace: 0 90 | allow-trailing-comment: true 91 | allow-separated-leading-comment: false 92 | allow-cuddle-declarations: false 93 | force-err-cuddling: false 94 | exclusions: 95 | generated: lax 96 | presets: 97 | - comments 98 | - common-false-positives 99 | - legacy 100 | - std-error-handling 101 | paths: 102 | - third_party$ 103 | - builtin$ 104 | - examples$ 105 | issues: 106 | uniq-by-line: true 107 | new-from-rev: "" 108 | new: false 109 | severity: 110 | default: error 111 | rules: 112 | - linters: 113 | - mnd 114 | severity: ignore 115 | formatters: 116 | enable: 117 | - gci 118 | - gofmt 119 | - gofumpt 120 | settings: 121 | gofmt: 122 | simplify: true 123 | goimports: 124 | local-prefixes: 125 | - github.com/omec-project 126 | exclusions: 127 | generated: lax 128 | paths: 129 | - third_party$ 130 | - builtin$ 131 | - examples$ 132 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | FROM golang:1.24.3-bookworm AS builder 7 | 8 | RUN apt-get update && \ 9 | apt-get -y install --no-install-recommends \ 10 | apt-transport-https \ 11 | ca-certificates \ 12 | gcc \ 13 | cmake \ 14 | autoconf \ 15 | libtool \ 16 | pkg-config \ 17 | libmnl-dev \ 18 | libyaml-dev && \ 19 | apt-get clean 20 | 21 | WORKDIR $GOPATH/src/amf 22 | 23 | COPY . . 24 | RUN make all 25 | 26 | FROM alpine:3.22 AS amf 27 | 28 | LABEL maintainer="Aether SD-Core " \ 29 | description="Aether open source 5G Core Network" \ 30 | version="Stage 3" 31 | 32 | ARG DEBUG_TOOLS 33 | 34 | RUN apk update && apk add --no-cache -U bash 35 | 36 | # Install debug tools ~ 50MB (if DEBUG_TOOLS is set to true) 37 | RUN if [ "$DEBUG_TOOLS" = "true" ]; then \ 38 | apk update && apk add --no-cache -U vim strace net-tools curl netcat-openbsd bind-tools; \ 39 | fi 40 | 41 | # Copy executable 42 | COPY --from=builder /go/src/amf/bin/* /usr/local/bin/. 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | # Copyright 2019 free5GC.org 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | # 7 | 8 | PROJECT_NAME := sdcore 9 | DOCKER_VERSION ?= $(shell cat ./VERSION) 10 | 11 | ## Docker related 12 | DOCKER_REGISTRY ?= 13 | DOCKER_REPOSITORY ?= 14 | DOCKER_TAG ?= ${DOCKER_VERSION} 15 | DOCKER_IMAGENAME := ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}${PROJECT_NAME}:${DOCKER_TAG} 16 | DOCKER_BUILDKIT ?= 1 17 | DOCKER_BUILD_ARGS ?= 18 | 19 | ## Docker labels. Only set ref and commit date if committed 20 | DOCKER_LABEL_VCS_URL ?= $(shell git remote get-url $(shell git remote)) 21 | DOCKER_LABEL_VCS_REF ?= $(shell git diff-index --quiet HEAD -- && git rev-parse HEAD || echo "unknown") 22 | DOCKER_LABEL_COMMIT_DATE ?= $(shell git diff-index --quiet HEAD -- && git show -s --format=%cd --date=iso-strict HEAD || echo "unknown" ) 23 | DOCKER_LABEL_BUILD_DATE ?= $(shell date -u "+%Y-%m-%dT%H:%M:%SZ") 24 | 25 | DOCKER_TARGETS ?= amf 26 | 27 | GO_BIN_PATH = bin 28 | GO_SRC_PATH = ./ 29 | C_BUILD_PATH = build 30 | ROOT_PATH = $(shell pwd) 31 | 32 | NF = $(GO_NF) 33 | GO_NF = amf 34 | 35 | NF_GO_FILES = $(shell find $(GO_SRC_PATH)/$(%) -name "*.go" ! -name "*_test.go") 36 | NF_GO_FILES_ALL = $(shell find $(GO_SRC_PATH)/$(%) -name "*.go") 37 | 38 | VERSION = $(shell git describe --tags) 39 | BUILD_TIME = $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") 40 | COMMIT_HASH = $(shell git submodule status | grep $(GO_SRC_PATH)/$(@F) | awk '{print $$(1)}' | cut -c1-8) 41 | COMMIT_TIME = $(shell cd $(GO_SRC_PATH) && git log --pretty="%ai" -1 | awk '{time=$$(1)"T"$$(2)"Z"; print time}') 42 | 43 | .PHONY: $(NF) clean docker-build docker-push 44 | 45 | .DEFAULT_GOAL: nfs 46 | 47 | nfs: $(NF) 48 | 49 | all: $(NF) 50 | 51 | $(GO_NF): % : $(GO_BIN_PATH)/% 52 | 53 | $(GO_BIN_PATH)/%: %.go $(NF_GO_FILES) 54 | # $(@F): The file-within-directory part of the file name of the target. 55 | @echo "Start building $(@F)...." 56 | cd $(GO_SRC_PATH)/ && \ 57 | CGO_ENABLED=0 go build -o $(ROOT_PATH)/$@ $(@F).go 58 | 59 | vpath %.go $(addprefix $(GO_SRC_PATH)/, $(GO_NF)) 60 | 61 | #test: $(NF_GO_FILES_ALL) 62 | # @echo "Start building $(@F)...." 63 | # cd $(GO_SRC_PATH)/ && \ 64 | # CGO_ENABLED=0 go test -o $(ROOT_PATH)/$@ 65 | 66 | clean: 67 | rm -rf $(addprefix $(GO_BIN_PATH)/, $(GO_NF)) 68 | rm -rf $(addprefix $(GO_SRC_PATH)/, $(addsuffix /$(C_BUILD_PATH), $(C_NF))) 69 | 70 | docker-build: 71 | @go mod vendor 72 | for target in $(DOCKER_TARGETS); do \ 73 | DOCKER_BUILDKIT=$(DOCKER_BUILDKIT) docker build $(DOCKER_BUILD_ARGS) \ 74 | --target $$target \ 75 | --tag ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}5gc-$$target:${DOCKER_TAG} \ 76 | --build-arg org_label_schema_version="${DOCKER_VERSION}" \ 77 | --build-arg org_label_schema_vcs_url="${DOCKER_LABEL_VCS_URL}" \ 78 | --build-arg org_label_schema_vcs_ref="${DOCKER_LABEL_VCS_REF}" \ 79 | --build-arg org_label_schema_build_date="${DOCKER_LABEL_BUILD_DATE}" \ 80 | --build-arg org_opencord_vcs_commit_date="${DOCKER_LABEL_COMMIT_DATE}" \ 81 | . \ 82 | || exit 1; \ 83 | done 84 | rm -rf vendor 85 | 86 | docker-push: 87 | for target in $(DOCKER_TARGETS); do \ 88 | docker push ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}5gc-$$target:${DOCKER_TAG}; \ 89 | done 90 | 91 | .coverage: 92 | rm -rf $(CURDIR)/.coverage 93 | mkdir -p $(CURDIR)/.coverage 94 | 95 | test: .coverage 96 | docker run --rm -v $(CURDIR):/amf -w /amf golang:latest \ 97 | go test \ 98 | -failfast \ 99 | -coverprofile=.coverage/coverage-unit.txt \ 100 | -covermode=atomic \ 101 | -v \ 102 | ./ ./... 103 | 104 | fmt: 105 | @go fmt ./... 106 | 107 | golint: 108 | @docker run --rm -v $(CURDIR):/app -w /app golangci/golangci-lint:latest golangci-lint run -v --config /app/.golangci.yml 109 | 110 | check-reuse: 111 | @docker run --rm -v $(CURDIR):/amf -w /amf omecproject/reuse-verify:latest reuse lint 112 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | amf 2 | Copyright 2021 Open Networking Foundation 3 | SPDX-License-Identifier: Apache-2.0 4 | 5 | 6 | The Initial Developer of the work is free5GC (https://www.free5gc.org/). 7 | At the time of forking, no copyright statements were found 8 | in the free5GC files or directories. 9 | Copyright 2019 free5GC.org. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 7 | [![Go Report Card](https://goreportcard.com/badge/github.com/omec-project/amf)](https://goreportcard.com/report/github.com/omec-project/amf) 8 | 9 | # amf 10 | It is a control plane function in the 5G core network. AMF supports termination 11 | of NAS signalling, NAS ciphering & integrity protection, registration 12 | management, connection management, mobility management, access authentication 13 | and authorization, security context management. 14 | 15 | ## AMF Block Diagram 16 | ![AMF Block Diagram](/docs/images/README-AMF.png) 17 | 18 | AMF takes configuration from Configuration Service. Configuration is handled at 19 | Network Slice level. Configuration (Network Slices) can be added, removed and 20 | deleted. AMF has prometheus interface to export metrics. Metrics include 21 | connected gNodeB's and its status. 22 | 23 | ## The SD-Core AMF currently supports the following functionalities: 24 | - Termination of RAN CP interface (N2) 25 | - Termination of NAS (N1), NAS ciphering and integrity protection 26 | - Registration management 27 | - Connection management 28 | - Reachability management 29 | - Mobility Management 30 | - Provide transport for SM messages between UE and SMF 31 | - Transparent proxy for routing SM messages 32 | - Access Authentication 33 | - Access Authorization 34 | 35 | ## Supported Procedures: 36 | - Registration/Deregistration 37 | - Registration update 38 | - UE initiated Service request 39 | - N2 Handover 40 | - Xn handover 41 | - PDU Establishment Request/Release 42 | - Paging 43 | - CN High Availibilty and Stateless session support 44 | - AMF metrics are available via metricfunc on the 5g Grafana dashboard 45 | 46 | ## Upcoming Changes in AMF 47 | 48 | 49 | 50 | Compliance of the 5G Network functions can be found at [5G Compliance](https://docs.sd-core.opennetworking.org/main/overview/3gpp-compliance-5g.html) 51 | 52 | ## Reach out to us thorugh 53 | 54 | 1. #sdcore-dev channel in [ONF Community Slack](https://onf-community.slack.com/) 55 | 2. Raise Github issues 56 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.6.6-dev 2 | -------------------------------------------------------------------------------- /VERSION.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | 3 | SPDX-License-Identifier: Apache-2.0 4 | -------------------------------------------------------------------------------- /amf.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Intel Corporation 2 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 3 | // Copyright 2019 free5GC.org 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | // 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | 14 | "github.com/omec-project/amf/logger" 15 | "github.com/omec-project/amf/service" 16 | "github.com/urfave/cli" 17 | ) 18 | 19 | var AMF = &service.AMF{} 20 | 21 | func main() { 22 | app := cli.NewApp() 23 | app.Name = "amf" 24 | logger.AppLog.Infoln(app.Name) 25 | app.Usage = "Access & Mobility Management function" 26 | app.UsageText = "amf -cfg " 27 | app.Action = action 28 | app.Flags = AMF.GetCliCmd() 29 | if err := app.Run(os.Args); err != nil { 30 | logger.AppLog.Fatalf("AMF run error: %v", err) 31 | } 32 | } 33 | 34 | func action(c *cli.Context) error { 35 | if err := AMF.Initialize(c); err != nil { 36 | logger.CfgLog.Errorf("%+v", err) 37 | return fmt.Errorf("failed to initialize") 38 | } 39 | 40 | AMF.WatchConfig() 41 | 42 | AMF.Start() 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /amfTest/amfcfg.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Intel Corporation 2 | # SPDX-FileCopyrightText: 2021 Open Networking Foundation 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | info: 8 | version: 1.0.0 9 | description: AMF initial local configuration 10 | 11 | configuration: 12 | amfName: AMF # the name of this AMF 13 | ngapIpList: # the IP list of N2 interfaces on this AMF 14 | - 127.0.0.1 15 | sbi: # Service-based interface information 16 | scheme: http # the protocol for sbi (http or https) 17 | registerIPv4: 127.0.0.18 # IP used to register to NRF 18 | bindingIPv4: 127.0.0.18 # IP used to bind the service 19 | port: 8000 # port used to bind the service 20 | tls: # the local path of TLS key 21 | key: /support/TLS/amf.pem # AMF TLS Certificate 22 | pem: /support/TLS/amf.pem # AMF TLS Private key 23 | serviceNameList: # the SBI services provided by this AMF, refer to TS 29.518 24 | - namf-comm # Namf_Communication service 25 | - namf-evts # Namf_EventExposure service 26 | - namf-mt # Namf_MT service 27 | - namf-loc # Namf_Location service 28 | - namf-oam # OAM service 29 | servedGuamiList: # Guami (Globally Unique AMF ID) list supported by this AMF 30 | # = 31 | - plmnId: # Public Land Mobile Network ID, = 32 | mcc: 208 # Mobile Country Code (3 digits string, digit: 0~9) 33 | mnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9) 34 | amfId: cafe00 # AMF identifier (3 bytes hex string, range: 000000~FFFFFF) 35 | supportTaiList: # the TAI (Tracking Area Identifier) list supported by this AMF 36 | - plmnId: # Public Land Mobile Network ID, = 37 | mcc: 208 # Mobile Country Code (3 digits string, digit: 0~9) 38 | mnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9) 39 | tac: 1 # Tracking Area Code (uinteger, range: 0~16777215) 40 | plmnSupportList: # the PLMNs (Public land mobile network) list supported by this AMF 41 | - plmnId: # Public Land Mobile Network ID, = 42 | mcc: 208 # Mobile Country Code (3 digits string, digit: 0~9) 43 | mnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9) 44 | snssaiList: # the S-NSSAI (Single Network Slice Selection Assistance Information) list supported by this AMF 45 | - sst: 1 # Slice/Service Type (uinteger, range: 0~255) 46 | sd: 010203 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF) 47 | - sst: 1 # Slice/Service Type (uinteger, range: 0~255) 48 | sd: 112233 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF) 49 | supportDnnList: # the DNN (Data Network Name) list supported by this AMF 50 | - internet 51 | nrfUri: http://127.0.0.10:8000 # a valid URI of NRF 52 | security: # NAS security parameters 53 | integrityOrder: # the priority of integrity algorithms 54 | - NIA2 55 | # - NIA0 56 | cipheringOrder: # the priority of ciphering algorithms 57 | - NEA0 58 | # - NEA2 59 | networkName: # the name of this core network 60 | full: Aether 61 | short: Aether 62 | networkFeatureSupport5GS: # 5gs Network Feature Support IE, refer to TS 24.501 63 | enable: true # append this IE in Registration accept or not 64 | imsVoPS: 0 # IMS voice over PS session indicator (uinteger, range: 0~1) 65 | emc: 0 # Emergency service support indicator for 3GPP access (uinteger, range: 0~3) 66 | emf: 0 # Emergency service fallback indicator for 3GPP access (uinteger, range: 0~3) 67 | iwkN26: 0 # Interworking without N26 interface indicator (uinteger, range: 0~1) 68 | mpsi: 0 # MPS indicator (uinteger, range: 0~1) 69 | emcN3: 0 # Emergency service support indicator for Non-3GPP access (uinteger, range: 0~1) 70 | mcsi: 0 # MCS indicator (uinteger, range: 0~1) 71 | t3502Value: 720 # timer value (seconds) at UE side 72 | t3512Value: 3600 # timer value (seconds) at UE side 73 | non3gppDeregistrationTimerValue: 3240 # timer value (seconds) at UE side 74 | # retransmission timer for paging message 75 | t3513: 76 | enable: true # true or false 77 | expireTime: 6s # default is 6 seconds 78 | maxRetryTimes: 4 # the max number of retransmission 79 | # retransmission timer for NAS Deregistration Request message 80 | t3522: 81 | enable: true # true or false 82 | expireTime: 6s # default is 6 seconds 83 | maxRetryTimes: 4 # the max number of retransmission 84 | # retransmission timer for NAS Registration Accept message 85 | t3550: 86 | enable: true # true or false 87 | expireTime: 6s # default is 6 seconds 88 | maxRetryTimes: 4 # the max number of retransmission 89 | # retransmission timer for NAS Authentication Request/Security Mode Command message 90 | t3560: 91 | enable: true # true or false 92 | expireTime: 6s # default is 6 seconds 93 | maxRetryTimes: 4 # the max number of retransmission 94 | # retransmission timer for NAS Notification message 95 | t3565: 96 | enable: true # true or false 97 | expireTime: 6s # default is 6 seconds 98 | maxRetryTimes: 4 # the max number of retransmission 99 | 100 | # the kind of log output 101 | # debugLevel: how detailed to output, value: trace, debug, info, warn, error, fatal, panic 102 | # ReportCaller: enable the caller report or not, value: true or false 103 | logger: 104 | AMF: 105 | debugLevel: info 106 | NAS: 107 | debugLevel: info 108 | FSM: 109 | debugLevel: info 110 | NGAP: 111 | debugLevel: info 112 | Aper: 113 | debugLevel: info 114 | OpenApi: 115 | debugLevel: info 116 | -------------------------------------------------------------------------------- /amfTest/amfcfg_with_custom_webui_url.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # SPDX-FileCopyrightText: 2024 Canonical Ltd. 3 | 4 | info: 5 | version: 1.0.0 6 | description: AMF initial local configuration 7 | 8 | configuration: 9 | amfName: AMF # the name of this AMF 10 | webuiUri: myspecialwebui:9872 # a valid URI of Webui 11 | 12 | 13 | -------------------------------------------------------------------------------- /amf_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | /* 6 | * AMF Unit Testcases 7 | * 8 | */ 9 | package main 10 | 11 | import ( 12 | "encoding/json" 13 | "log" 14 | "os" 15 | "testing" 16 | "time" 17 | 18 | "github.com/omec-project/amf/consumer" 19 | "github.com/omec-project/amf/factory" 20 | "github.com/omec-project/amf/service" 21 | protos "github.com/omec-project/config5g/proto/sdcoreConfig" 22 | "github.com/omec-project/openapi/models" 23 | "github.com/stretchr/testify/require" 24 | ) 25 | 26 | var AMFTest = &service.AMF{} 27 | 28 | func init() { 29 | if err := os.Setenv("POD_IP", "127.0.0.1"); err != nil { 30 | log.Printf("Could not set env POD_IP: %+v", err) 31 | } 32 | if err := factory.InitConfigFactory("amfTest/amfcfg.yaml"); err != nil { 33 | log.Printf("Could not InitConfigFactory: %+v", err) 34 | } 35 | } 36 | 37 | func GetNetworkSliceConfig() *protos.NetworkSliceResponse { 38 | var rsp protos.NetworkSliceResponse 39 | 40 | rsp.NetworkSlice = make([]*protos.NetworkSlice, 0) 41 | 42 | ns := protos.NetworkSlice{} 43 | ns.OperationType = protos.OpType_SLICE_ADD 44 | slice := protos.NSSAI{Sst: "1", Sd: "010203"} 45 | ns.Nssai = &slice 46 | 47 | site := protos.SiteInfo{SiteName: "siteOne", Gnb: make([]*protos.GNodeB, 0), Plmn: new(protos.PlmnId)} 48 | gNb := protos.GNodeB{Name: "gnb", Tac: 1} 49 | site.Gnb = append(site.Gnb, &gNb) 50 | site.Plmn.Mcc = "208" 51 | site.Plmn.Mnc = "93" 52 | ns.Site = &site 53 | 54 | rsp.NetworkSlice = append(rsp.NetworkSlice, &ns) 55 | return &rsp 56 | } 57 | 58 | func TestInitialConfig(t *testing.T) { 59 | factory.AmfConfig.Configuration.PlmnSupportList = nil 60 | factory.AmfConfig.Configuration.ServedGumaiList = nil 61 | factory.AmfConfig.Configuration.SupportTAIList = nil 62 | Rsp := make(chan *protos.NetworkSliceResponse, 1) 63 | 64 | go func() { 65 | AMFTest.UpdateConfig(Rsp) 66 | }() 67 | Rsp <- GetNetworkSliceConfig() 68 | 69 | time.Sleep(2 * time.Second) 70 | close(Rsp) 71 | 72 | if factory.AmfConfig.Configuration.PlmnSupportList != nil && 73 | factory.AmfConfig.Configuration.ServedGumaiList != nil && 74 | factory.AmfConfig.Configuration.SupportTAIList != nil { 75 | t.Logf("test passed") 76 | } else { 77 | t.Errorf("test failed") 78 | } 79 | } 80 | 81 | // data in JSON format which 82 | // is to be decoded 83 | var Data = []byte(`{ 84 | "NetworkSlice": [ 85 | { 86 | "Name": "siteOne", 87 | "Nssai": {"Sst": "1", "Sd": "010203"}, 88 | "Site": { 89 | "SiteName": "siteOne", 90 | "Gnb": [ 91 | {"Name": "gnb1", "Tac": 1}, 92 | {"Name": "gnb2", "Tac": 2} 93 | ], 94 | "Plmn": {"mcc": "208", "mnc": "93"} 95 | } 96 | } 97 | ]}`) 98 | 99 | func TestUpdateConfig(t *testing.T) { 100 | var nrp protos.NetworkSliceResponse 101 | err := json.Unmarshal(Data, &nrp) 102 | if err != nil { 103 | panic(err) 104 | } 105 | Rsp := make(chan *protos.NetworkSliceResponse) 106 | go func() { 107 | Rsp <- &nrp 108 | }() 109 | go func() { 110 | AMFTest.UpdateConfig(Rsp) 111 | }() 112 | 113 | time.Sleep(2 * time.Second) 114 | if len(factory.AmfConfig.Configuration.SupportTAIList) == 2 { 115 | t.Logf("test passed") 116 | } else { 117 | t.Errorf("test failed") 118 | } 119 | } 120 | 121 | func TestRegisterNF(t *testing.T) { 122 | // Save current function and restore at the end: 123 | origRegisterNFInstance := consumer.SendRegisterNFInstance 124 | // origSearchNFInstances := consumer.SendSearchNFInstances 125 | origUpdateNFInstance := consumer.SendUpdateNFInstance 126 | defer func() { 127 | consumer.SendRegisterNFInstance = origRegisterNFInstance 128 | // consumer.SendSearchNFInstances = origSearchNFInstances 129 | consumer.SendUpdateNFInstance = origUpdateNFInstance 130 | }() 131 | t.Logf("test case TestRegisterNF") 132 | var prof models.NfProfile 133 | consumer.SendRegisterNFInstance = func(nrfUri string, nfInstanceId string, profile models.NfProfile) (models.NfProfile, string, string, error) { 134 | prof = profile 135 | prof.HeartBeatTimer = 1 136 | t.Logf("Test RegisterNFInstance called") 137 | return prof, "", "", nil 138 | } 139 | /*consumer.SendSearchNFInstances = func(nrfUri string, targetNfType, requestNfType models.NfType, param Nnrf_NFDiscovery.SearchNFInstancesParamOpts) (*models.SearchResult, error) { 140 | fmt.Printf("Test SearchNFInstance called\n") 141 | return &models.SearchResult{}, nil 142 | }*/ 143 | consumer.SendUpdateNFInstance = func(patchItem []models.PatchItem) (nfProfile models.NfProfile, problemDetails *models.ProblemDetails, err error) { 144 | return prof, nil, nil 145 | } 146 | go AMFTest.SendNFProfileUpdateToNrf() 147 | service.RocUpdateConfigChannel <- true 148 | time.Sleep(5 * time.Second) 149 | require.Equal(t, service.KeepAliveTimer != nil, true) 150 | 151 | /*service.RocUpdateConfigChannel <- false 152 | time.Sleep(1 * time.Second) 153 | require.Equal(t, service.KeepAliveTimer == nil, true) 154 | */ 155 | } 156 | -------------------------------------------------------------------------------- /communication/api_individual_subscription_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Communication 8 | * 9 | * AMF Communication Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package communication 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | "github.com/omec-project/amf/producer" 23 | "github.com/omec-project/openapi" 24 | "github.com/omec-project/openapi/models" 25 | "github.com/omec-project/util/httpwrapper" 26 | ) 27 | 28 | // AMFStatusChangeSubscribeModify - Namf_Communication AMF Status Change Subscribe Modify service Operation 29 | func HTTPAMFStatusChangeSubscribeModify(c *gin.Context) { 30 | var subscriptionData models.SubscriptionData 31 | 32 | requestBody, err := c.GetRawData() 33 | if err != nil { 34 | logger.CommLog.Errorf("Get Request Body error: %+v", err) 35 | problemDetail := models.ProblemDetails{ 36 | Title: "System failure", 37 | Status: http.StatusInternalServerError, 38 | Detail: err.Error(), 39 | Cause: "SYSTEM_FAILURE", 40 | } 41 | c.JSON(http.StatusInternalServerError, problemDetail) 42 | return 43 | } 44 | 45 | err = openapi.Deserialize(&subscriptionData, requestBody, "application/json") 46 | if err != nil { 47 | problemDetail := "[Request Body] " + err.Error() 48 | rsp := models.ProblemDetails{ 49 | Title: "Malformed request syntax", 50 | Status: http.StatusBadRequest, 51 | Detail: problemDetail, 52 | } 53 | logger.CommLog.Errorln(problemDetail) 54 | c.JSON(http.StatusBadRequest, rsp) 55 | return 56 | } 57 | 58 | req := httpwrapper.NewRequest(c.Request, subscriptionData) 59 | req.Params["subscriptionId"] = c.Params.ByName("subscriptionId") 60 | 61 | rsp := producer.HandleAMFStatusChangeSubscribeModify(req) 62 | 63 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 64 | if err != nil { 65 | logger.CommLog.Errorln(err) 66 | problemDetails := models.ProblemDetails{ 67 | Status: http.StatusInternalServerError, 68 | Cause: "SYSTEM_FAILURE", 69 | Detail: err.Error(), 70 | } 71 | c.JSON(http.StatusInternalServerError, problemDetails) 72 | } else { 73 | c.Data(rsp.Status, "application/json", responseBody) 74 | } 75 | } 76 | 77 | // AMFStatusChangeUnSubscribe - Namf_Communication AMF Status Change UnSubscribe service Operation 78 | func HTTPAMFStatusChangeUnSubscribe(c *gin.Context) { 79 | req := httpwrapper.NewRequest(c.Request, nil) 80 | req.Params["subscriptionId"] = c.Params.ByName("subscriptionId") 81 | 82 | rsp := producer.HandleAMFStatusChangeUnSubscribeRequest(req) 83 | 84 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 85 | if err != nil { 86 | logger.CommLog.Errorln(err) 87 | problemDetails := models.ProblemDetails{ 88 | Status: http.StatusInternalServerError, 89 | Cause: "SYSTEM_FAILURE", 90 | Detail: err.Error(), 91 | } 92 | c.JSON(http.StatusInternalServerError, problemDetails) 93 | } else { 94 | c.Data(rsp.Status, "application/json", responseBody) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /communication/api_n1_n2_individual_subscription_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Communication 8 | * 9 | * AMF Communication Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package communication 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | "github.com/omec-project/amf/producer" 23 | "github.com/omec-project/openapi" 24 | "github.com/omec-project/openapi/models" 25 | "github.com/omec-project/util/httpwrapper" 26 | ) 27 | 28 | // N1N2MessageUnSubscribe - Namf_Communication N1N2 Message UnSubscribe (UE Specific) service Operation 29 | func HTTPN1N2MessageUnSubscribe(c *gin.Context) { 30 | req := httpwrapper.NewRequest(c.Request, nil) 31 | req.Params["ueContextId"] = c.Params.ByName("ueContextId") 32 | req.Params["subscriptionId"] = c.Params.ByName("subscriptionId") 33 | 34 | rsp := producer.HandleN1N2MessageUnSubscribeRequest(req) 35 | 36 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 37 | if err != nil { 38 | logger.CommLog.Errorln(err) 39 | problemDetails := models.ProblemDetails{ 40 | Status: http.StatusInternalServerError, 41 | Cause: "SYSTEM_FAILURE", 42 | Detail: err.Error(), 43 | } 44 | c.JSON(http.StatusInternalServerError, problemDetails) 45 | } else { 46 | c.Data(rsp.Status, "application/json", responseBody) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /communication/api_n1_n2_message_collection_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Communication 8 | * 9 | * AMF Communication Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package communication 16 | 17 | import ( 18 | "fmt" 19 | "net/http" 20 | "strings" 21 | 22 | "github.com/gin-gonic/gin" 23 | "github.com/omec-project/amf/logger" 24 | "github.com/omec-project/amf/producer" 25 | "github.com/omec-project/openapi" 26 | "github.com/omec-project/openapi/models" 27 | "github.com/omec-project/util/httpwrapper" 28 | ) 29 | 30 | // N1N2MessageTransfer - Namf_Communication N1N2 Message Transfer (UE Specific) service Operation 31 | func HTTPN1N2MessageTransfer(c *gin.Context) { 32 | var n1n2MessageTransferRequest models.N1N2MessageTransferRequest 33 | n1n2MessageTransferRequest.JsonData = new(models.N1N2MessageTransferReqData) 34 | 35 | requestBody, err := c.GetRawData() 36 | if err != nil { 37 | problemDetail := models.ProblemDetails{ 38 | Title: "System failure", 39 | Status: http.StatusInternalServerError, 40 | Detail: err.Error(), 41 | Cause: "SYSTEM_FAILURE", 42 | } 43 | logger.CommLog.Errorf("Get Request Body error: %+v", err) 44 | c.JSON(http.StatusInternalServerError, problemDetail) 45 | return 46 | } 47 | 48 | contentType := c.GetHeader("Content-Type") 49 | s := strings.Split(contentType, ";") 50 | switch s[0] { 51 | case "application/json": 52 | err = fmt.Errorf("N1 and N2 datas are both Empty in N1N2MessgeTransfer") 53 | case "multipart/related": 54 | err = openapi.Deserialize(&n1n2MessageTransferRequest, requestBody, contentType) 55 | default: 56 | err = fmt.Errorf("wrong content type") 57 | } 58 | 59 | if err != nil { 60 | problemDetail := "[Request Body] " + err.Error() 61 | rsp := models.ProblemDetails{ 62 | Title: "Malformed request syntax", 63 | Status: http.StatusBadRequest, 64 | Detail: problemDetail, 65 | } 66 | logger.CommLog.Errorln(problemDetail) 67 | c.JSON(http.StatusBadRequest, rsp) 68 | return 69 | } 70 | 71 | req := httpwrapper.NewRequest(c.Request, n1n2MessageTransferRequest) 72 | req.Params["ueContextId"] = c.Params.ByName("ueContextId") 73 | req.Params["reqUri"] = c.Request.RequestURI 74 | 75 | rsp := producer.HandleN1N2MessageTransferRequest(req) 76 | 77 | for key, val := range rsp.Header { 78 | c.Header(key, val[0]) 79 | } 80 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 81 | if err != nil { 82 | logger.CommLog.Errorln(err) 83 | problemDetails := models.ProblemDetails{ 84 | Status: http.StatusInternalServerError, 85 | Cause: "SYSTEM_FAILURE", 86 | Detail: err.Error(), 87 | } 88 | c.JSON(http.StatusInternalServerError, problemDetails) 89 | } else { 90 | c.Data(rsp.Status, "application/json", responseBody) 91 | } 92 | } 93 | 94 | func HTTPN1N2MessageTransferStatus(c *gin.Context) { 95 | req := httpwrapper.NewRequest(c.Request, nil) 96 | req.Params["ueContextId"] = c.Params.ByName("ueContextId") 97 | req.Params["reqUri"] = c.Request.RequestURI 98 | 99 | rsp := producer.HandleN1N2MessageTransferStatusRequest(req) 100 | 101 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 102 | if err != nil { 103 | logger.CommLog.Errorln(err) 104 | problemDetails := models.ProblemDetails{ 105 | Status: http.StatusInternalServerError, 106 | Cause: "SYSTEM_FAILURE", 107 | Detail: err.Error(), 108 | } 109 | c.JSON(http.StatusInternalServerError, problemDetails) 110 | } else { 111 | c.Data(rsp.Status, "application/json", responseBody) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /communication/api_n1_n2_subscriptions_collection_for_individual_ue_contexts_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Communication 8 | * 9 | * AMF Communication Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package communication 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | "github.com/omec-project/amf/producer" 23 | "github.com/omec-project/openapi" 24 | "github.com/omec-project/openapi/models" 25 | "github.com/omec-project/util/httpwrapper" 26 | ) 27 | 28 | func HTTPN1N2MessageSubscribe(c *gin.Context) { 29 | var ueN1N2InfoSubscriptionCreateData models.UeN1N2InfoSubscriptionCreateData 30 | 31 | requestBody, err := c.GetRawData() 32 | if err != nil { 33 | logger.CommLog.Errorf("Get Request Body error: %+v", err) 34 | problemDetail := models.ProblemDetails{ 35 | Title: "System failure", 36 | Status: http.StatusInternalServerError, 37 | Detail: err.Error(), 38 | Cause: "SYSTEM_FAILURE", 39 | } 40 | c.JSON(http.StatusInternalServerError, problemDetail) 41 | return 42 | } 43 | 44 | err = openapi.Deserialize(&ueN1N2InfoSubscriptionCreateData, requestBody, "application/json") 45 | if err != nil { 46 | problemDetail := "[Request Body] " + err.Error() 47 | rsp := models.ProblemDetails{ 48 | Title: "Malformed request syntax", 49 | Status: http.StatusBadRequest, 50 | Detail: problemDetail, 51 | } 52 | logger.CommLog.Errorln(problemDetail) 53 | c.JSON(http.StatusBadRequest, rsp) 54 | return 55 | } 56 | 57 | req := httpwrapper.NewRequest(c.Request, ueN1N2InfoSubscriptionCreateData) 58 | req.Params["ueContextId"] = c.Params.ByName("ueContextId") 59 | 60 | rsp := producer.HandleN1N2MessageSubscirbeRequest(req) 61 | 62 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 63 | if err != nil { 64 | logger.CommLog.Errorln(err) 65 | problemDetails := models.ProblemDetails{ 66 | Status: http.StatusInternalServerError, 67 | Cause: "SYSTEM_FAILURE", 68 | Detail: err.Error(), 69 | } 70 | c.JSON(http.StatusInternalServerError, problemDetails) 71 | } else { 72 | c.Data(rsp.Status, "application/json", responseBody) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /communication/api_non_uen2_message_notification_individual_subscription_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Communication 8 | * 9 | * AMF Communication Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package communication 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | ) 23 | 24 | // NonUeN2InfoUnSubscribe - Namf_Communication Non UE N2 Info UnSubscribe service Operation 25 | func HTTPNonUeN2InfoUnSubscribe(c *gin.Context) { 26 | logger.CommLog.Warnf("Handle Non Ue N2 Info UnSubscribe is not implemented.") 27 | c.JSON(http.StatusOK, gin.H{}) 28 | } 29 | -------------------------------------------------------------------------------- /communication/api_non_uen2_messages_collection_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Communication 8 | * 9 | * AMF Communication Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package communication 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | ) 23 | 24 | // NonUeN2MessageTransfer - Namf_Communication Non UE N2 Message Transfer service Operation 25 | func HTTPNonUeN2MessageTransfer(c *gin.Context) { 26 | logger.CommLog.Warnf("Handle Non Ue N2 Message Transfer is not implemented.") 27 | c.JSON(http.StatusOK, gin.H{}) 28 | } 29 | -------------------------------------------------------------------------------- /communication/api_non_uen2_messages_subscriptions_collection_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Communication 8 | * 9 | * AMF Communication Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package communication 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | ) 23 | 24 | // NonUeN2InfoSubscribe - Namf_Communication Non UE N2 Info Subscribe service Operation 25 | func HTTPNonUeN2InfoSubscribe(c *gin.Context) { 26 | logger.CommLog.Warnf("Handle Non Ue N2 Info Subscribe is not implemented.") 27 | c.JSON(http.StatusOK, gin.H{}) 28 | } 29 | -------------------------------------------------------------------------------- /communication/api_subscriptions_collection_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Communication 8 | * 9 | * AMF Communication Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package communication 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | "github.com/omec-project/amf/producer" 23 | "github.com/omec-project/openapi" 24 | "github.com/omec-project/openapi/models" 25 | "github.com/omec-project/util/httpwrapper" 26 | ) 27 | 28 | // AMFStatusChangeSubscribe - Namf_Communication AMF Status Change Subscribe service Operation 29 | func HTTPAMFStatusChangeSubscribe(c *gin.Context) { 30 | var subscriptionData models.SubscriptionData 31 | 32 | requestBody, err := c.GetRawData() 33 | if err != nil { 34 | logger.CommLog.Errorf("Get Request Body error: %+v", err) 35 | problemDetail := models.ProblemDetails{ 36 | Title: "System failure", 37 | Status: http.StatusInternalServerError, 38 | Detail: err.Error(), 39 | Cause: "SYSTEM_FAILURE", 40 | } 41 | c.JSON(http.StatusInternalServerError, problemDetail) 42 | return 43 | } 44 | 45 | err = openapi.Deserialize(&subscriptionData, requestBody, "application/json") 46 | if err != nil { 47 | problemDetail := "[Request Body] " + err.Error() 48 | rsp := models.ProblemDetails{ 49 | Title: "Malformed request syntax", 50 | Status: http.StatusBadRequest, 51 | Detail: problemDetail, 52 | } 53 | logger.CommLog.Errorln(problemDetail) 54 | c.JSON(http.StatusBadRequest, rsp) 55 | return 56 | } 57 | 58 | req := httpwrapper.NewRequest(c.Request, subscriptionData) 59 | rsp := producer.HandleAMFStatusChangeSubscribeRequest(req) 60 | 61 | for key, val := range rsp.Header { 62 | c.Header(key, val[0]) 63 | } 64 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 65 | if err != nil { 66 | logger.CommLog.Errorln(err) 67 | problemDetails := models.ProblemDetails{ 68 | Status: http.StatusInternalServerError, 69 | Cause: "SYSTEM_FAILURE", 70 | Detail: err.Error(), 71 | } 72 | c.JSON(http.StatusInternalServerError, problemDetails) 73 | } else { 74 | c.Data(rsp.Status, "application/json", responseBody) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /communication/routers.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Intel Corporation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | /* 8 | * Namf_Communication 9 | * 10 | * AMF Communication Service 11 | * 12 | * API version: 1.0.0 13 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 14 | */ 15 | 16 | package communication 17 | 18 | import ( 19 | "net/http" 20 | "strings" 21 | 22 | "github.com/gin-gonic/gin" 23 | "github.com/omec-project/amf/logger" 24 | utilLogger "github.com/omec-project/util/logger" 25 | ) 26 | 27 | // Route is the information for every URI. 28 | type Route struct { 29 | // Name is the name of this Route. 30 | Name string 31 | // Method is the string for the HTTP method. ex) GET, POST etc.. 32 | Method string 33 | // Pattern is the pattern of the URI. 34 | Pattern string 35 | // HandlerFunc is the handler function of this route. 36 | HandlerFunc gin.HandlerFunc 37 | } 38 | 39 | // Routes is the list of the generated Route. 40 | type Routes []Route 41 | 42 | // NewRouter returns a new router. 43 | func NewRouter() *gin.Engine { 44 | router := utilLogger.NewGinWithZap(logger.GinLog) 45 | AddService(router) 46 | return router 47 | } 48 | 49 | func AddService(engine *gin.Engine) *gin.RouterGroup { 50 | group := engine.Group("/namf-comm/v1") 51 | 52 | for _, route := range routes { 53 | switch route.Method { 54 | case "GET": 55 | group.GET(route.Pattern, route.HandlerFunc) 56 | case "POST": 57 | group.POST(route.Pattern, route.HandlerFunc) 58 | case "PUT": 59 | group.PUT(route.Pattern, route.HandlerFunc) 60 | case "DELETE": 61 | group.DELETE(route.Pattern, route.HandlerFunc) 62 | } 63 | } 64 | return group 65 | } 66 | 67 | // Index is the index handler. 68 | func Index(c *gin.Context) { 69 | c.String(http.StatusOK, "Hello World!") 70 | } 71 | 72 | var routes = Routes{ 73 | { 74 | "Index", 75 | "GET", 76 | "/", 77 | Index, 78 | }, 79 | 80 | { 81 | "AMFStatusChangeSubscribeModify", 82 | strings.ToUpper("Put"), 83 | "/subscriptions/:subscriptionId", 84 | HTTPAMFStatusChangeSubscribeModify, 85 | }, 86 | 87 | { 88 | "AMFStatusChangeUnSubscribe", 89 | strings.ToUpper("Delete"), 90 | "/subscriptions/:subscriptionId", 91 | HTTPAMFStatusChangeUnSubscribe, 92 | }, 93 | 94 | { 95 | "CreateUEContext", 96 | strings.ToUpper("Put"), 97 | "/ue-contexts/:ueContextId", 98 | HTTPCreateUEContext, 99 | }, 100 | 101 | { 102 | "EBIAssignment", 103 | strings.ToUpper("Post"), 104 | "/ue-contexts/:ueContextId/assign-ebi", 105 | HTTPEBIAssignment, 106 | }, 107 | 108 | { 109 | "RegistrationStatusUpdate", 110 | strings.ToUpper("Post"), 111 | "/ue-contexts/:ueContextId/transfer-update", 112 | HTTPRegistrationStatusUpdate, 113 | }, 114 | 115 | { 116 | "ReleaseUEContext", 117 | strings.ToUpper("Post"), 118 | "/ue-contexts/:ueContextId/release", 119 | HTTPReleaseUEContext, 120 | }, 121 | 122 | { 123 | "UEContextTransfer", 124 | strings.ToUpper("Post"), 125 | "/ue-contexts/:ueContextId/transfer", 126 | HTTPUEContextTransfer, 127 | }, 128 | 129 | { 130 | "N1N2MessageUnSubscribe", 131 | strings.ToUpper("Delete"), 132 | "/ue-contexts/:ueContextId/n1-n2-messages/subscriptions/:subscriptionId", 133 | HTTPN1N2MessageUnSubscribe, 134 | }, 135 | 136 | { 137 | "N1N2MessageTransfer", 138 | strings.ToUpper("Post"), 139 | "/ue-contexts/:ueContextId/n1-n2-messages", 140 | HTTPN1N2MessageTransfer, 141 | }, 142 | 143 | { 144 | "N1N2MessageTransferStatus", 145 | strings.ToUpper("Get"), 146 | "/ue-contexts/:ueContextId/n1-n2-messages/:n1N2MessageId", 147 | HTTPN1N2MessageTransferStatus, 148 | }, 149 | 150 | { 151 | "N1N2MessageSubscribe", 152 | strings.ToUpper("Post"), 153 | "/ue-contexts/:ueContextId/n1-n2-messages/subscriptions", 154 | HTTPN1N2MessageSubscribe, 155 | }, 156 | 157 | { 158 | "NonUeN2InfoUnSubscribe", 159 | strings.ToUpper("Delete"), 160 | "/non-ue-n2-messages/subscriptions/:n2NotifySubscriptionId", 161 | HTTPNonUeN2InfoUnSubscribe, 162 | }, 163 | 164 | { 165 | "NonUeN2MessageTransfer", 166 | strings.ToUpper("Post"), 167 | "/non-ue-n2-messages/transfer", 168 | HTTPNonUeN2MessageTransfer, 169 | }, 170 | 171 | { 172 | "NonUeN2InfoSubscribe", 173 | strings.ToUpper("Post"), 174 | "/non-ue-n2-messages/subscriptions", 175 | HTTPNonUeN2InfoSubscribe, 176 | }, 177 | 178 | { 179 | "AMFStatusChangeSubscribe", 180 | strings.ToUpper("Post"), 181 | "/subscriptions", 182 | HTTPAMFStatusChangeSubscribe, 183 | }, 184 | } 185 | -------------------------------------------------------------------------------- /consumer/am_policy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package consumer 7 | 8 | import ( 9 | "context" 10 | "regexp" 11 | "time" 12 | 13 | amf_context "github.com/omec-project/amf/context" 14 | "github.com/omec-project/amf/logger" 15 | "github.com/omec-project/openapi" 16 | "github.com/omec-project/openapi/Npcf_AMPolicy" 17 | "github.com/omec-project/openapi/models" 18 | ) 19 | 20 | func AMPolicyControlCreate(ue *amf_context.AmfUe, anType models.AccessType) (*models.ProblemDetails, error) { 21 | configuration := Npcf_AMPolicy.NewConfiguration() 22 | configuration.SetBasePath(ue.PcfUri) 23 | client := Npcf_AMPolicy.NewAPIClient(configuration) 24 | 25 | amfSelf := amf_context.AMF_Self() 26 | 27 | policyAssociationRequest := models.PolicyAssociationRequest{ 28 | NotificationUri: amfSelf.GetIPv4Uri() + "/namf-callback/v1/am-policy/", 29 | Supi: ue.Supi, 30 | Pei: ue.Pei, 31 | Gpsi: ue.Gpsi, 32 | AccessType: anType, 33 | ServingPlmn: &models.NetworkId{ 34 | Mcc: ue.PlmnId.Mcc, 35 | Mnc: ue.PlmnId.Mnc, 36 | }, 37 | Guami: &amfSelf.ServedGuamiList[0], 38 | } 39 | 40 | if ue.AccessAndMobilitySubscriptionData != nil { 41 | policyAssociationRequest.Rfsp = ue.AccessAndMobilitySubscriptionData.RfspIndex 42 | } 43 | 44 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 45 | defer cancel() 46 | 47 | res, httpResp, localErr := client.DefaultApi.PoliciesPost(ctx, policyAssociationRequest) 48 | if localErr == nil { 49 | locationHeader := httpResp.Header.Get("Location") 50 | logger.ConsumerLog.Debugf("location header: %+v", locationHeader) 51 | ue.AmPolicyUri = locationHeader 52 | 53 | re := regexp.MustCompile("/policies/.*") 54 | match := re.FindStringSubmatch(locationHeader) 55 | 56 | ue.PolicyAssociationId = match[0][10:] 57 | ue.AmPolicyAssociation = &res 58 | 59 | if res.Triggers != nil { 60 | for _, trigger := range res.Triggers { 61 | if trigger == models.RequestTrigger_LOC_CH { 62 | ue.RequestTriggerLocationChange = true 63 | } 64 | //if trigger == models.RequestTrigger_PRA_CH { 65 | // TODO: Presence Reporting Area handling (TS 23.503 6.1.2.5, TS 23.501 5.6.11) 66 | //} 67 | } 68 | } 69 | 70 | logger.ConsumerLog.Debugf("UE AM Policy Association ID: %s", ue.PolicyAssociationId) 71 | logger.ConsumerLog.Debugf("AmPolicyAssociation: %+v", ue.AmPolicyAssociation) 72 | } else if httpResp != nil { 73 | if httpResp.Status != localErr.Error() { 74 | return nil, localErr 75 | } 76 | problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 77 | return &problem, nil 78 | } else { 79 | return nil, openapi.ReportError("server no response") 80 | } 81 | return nil, nil 82 | } 83 | 84 | func AMPolicyControlUpdate(ue *amf_context.AmfUe, updateRequest models.PolicyAssociationUpdateRequest) ( 85 | problemDetails *models.ProblemDetails, err error, 86 | ) { 87 | configuration := Npcf_AMPolicy.NewConfiguration() 88 | configuration.SetBasePath(ue.PcfUri) 89 | client := Npcf_AMPolicy.NewAPIClient(configuration) 90 | 91 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 92 | defer cancel() 93 | 94 | res, httpResp, localErr := client.DefaultApi.PoliciesPolAssoIdUpdatePost( 95 | ctx, ue.PolicyAssociationId, updateRequest) 96 | if localErr == nil { 97 | if res.ServAreaRes != nil { 98 | ue.AmPolicyAssociation.ServAreaRes = res.ServAreaRes 99 | } 100 | if res.Rfsp != 0 { 101 | ue.AmPolicyAssociation.Rfsp = res.Rfsp 102 | } 103 | ue.AmPolicyAssociation.Triggers = res.Triggers 104 | ue.RequestTriggerLocationChange = false 105 | for _, trigger := range res.Triggers { 106 | if trigger == models.RequestTrigger_LOC_CH { 107 | ue.RequestTriggerLocationChange = true 108 | } 109 | // if trigger == models.RequestTrigger_PRA_CH { 110 | // TODO: Presence Reporting Area handling (TS 23.503 6.1.2.5, TS 23.501 5.6.11) 111 | // } 112 | } 113 | return problemDetails, err 114 | } else if httpResp != nil { 115 | if httpResp.Status != localErr.Error() { 116 | err = localErr 117 | return problemDetails, err 118 | } 119 | problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 120 | problemDetails = &problem 121 | } else { 122 | err = openapi.ReportError("server no response") 123 | } 124 | return problemDetails, err 125 | } 126 | 127 | func AMPolicyControlDelete(ue *amf_context.AmfUe) (problemDetails *models.ProblemDetails, err error) { 128 | configuration := Npcf_AMPolicy.NewConfiguration() 129 | configuration.SetBasePath(ue.PcfUri) 130 | client := Npcf_AMPolicy.NewAPIClient(configuration) 131 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 132 | defer cancel() 133 | 134 | httpResp, localErr := client.DefaultApi.PoliciesPolAssoIdDelete(ctx, ue.PolicyAssociationId) 135 | if localErr == nil { 136 | ue.RemoveAmPolicyAssociation() 137 | } else if httpResp != nil { 138 | if httpResp.Status != localErr.Error() { 139 | err = localErr 140 | return 141 | } 142 | problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 143 | problemDetails = &problem 144 | } else { 145 | err = openapi.ReportError("server no response") 146 | } 147 | 148 | return 149 | } 150 | -------------------------------------------------------------------------------- /consumer/nf_discovery.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package consumer 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "net/http" 12 | 13 | amf_context "github.com/omec-project/amf/context" 14 | "github.com/omec-project/amf/logger" 15 | "github.com/omec-project/amf/util" 16 | "github.com/omec-project/openapi/Nnrf_NFDiscovery" 17 | "github.com/omec-project/openapi/models" 18 | nrfCache "github.com/omec-project/openapi/nrfcache" 19 | ) 20 | 21 | func SendSearchNFInstances(nrfUri string, targetNfType, requestNfType models.NfType, 22 | param *Nnrf_NFDiscovery.SearchNFInstancesParamOpts, 23 | ) (models.SearchResult, error) { 24 | if amf_context.AMF_Self().EnableNrfCaching { 25 | return nrfCache.SearchNFInstances(nrfUri, targetNfType, requestNfType, param) 26 | } else { 27 | return SendNfDiscoveryToNrf(nrfUri, targetNfType, requestNfType, param) 28 | } 29 | } 30 | 31 | func SendNfDiscoveryToNrf(nrfUri string, targetNfType, requestNfType models.NfType, 32 | param *Nnrf_NFDiscovery.SearchNFInstancesParamOpts, 33 | ) (models.SearchResult, error) { 34 | // Set client and set url 35 | configuration := Nnrf_NFDiscovery.NewConfiguration() 36 | configuration.SetBasePath(nrfUri) 37 | client := Nnrf_NFDiscovery.NewAPIClient(configuration) 38 | 39 | result, res, err := client.NFInstancesStoreApi.SearchNFInstances(context.TODO(), targetNfType, requestNfType, param) 40 | if res != nil && res.StatusCode == http.StatusTemporaryRedirect { 41 | err = fmt.Errorf("temporary Redirect For Non NRF Consumer") 42 | } 43 | defer func() { 44 | if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil { 45 | err = fmt.Errorf("SearchNFInstances' response body cannot close: %+w", bodyCloseErr) 46 | } 47 | }() 48 | 49 | amfSelf := amf_context.AMF_Self() 50 | 51 | var nrfSubData models.NrfSubscriptionData 52 | var problemDetails *models.ProblemDetails 53 | for _, nfProfile := range result.NfInstances { 54 | // checking whether the AMF subscribed to this target nfinstanceid or not 55 | if _, ok := amfSelf.NfStatusSubscriptions.Load(nfProfile.NfInstanceId); !ok { 56 | nrfSubscriptionData := models.NrfSubscriptionData{ 57 | NfStatusNotificationUri: fmt.Sprintf("%s/namf-callback/v1/nf-status-notify", amfSelf.GetIPv4Uri()), 58 | SubscrCond: &models.NfInstanceIdCond{NfInstanceId: nfProfile.NfInstanceId}, 59 | ReqNfType: requestNfType, 60 | } 61 | nrfSubData, problemDetails, err = SendCreateSubscription(nrfUri, nrfSubscriptionData) 62 | if problemDetails != nil { 63 | logger.ConsumerLog.Errorf("SendCreateSubscription to NRF, Problem[%+v]", problemDetails) 64 | } else if err != nil { 65 | logger.ConsumerLog.Errorf("SendCreateSubscription Error[%+v]", err) 66 | } 67 | amfSelf.NfStatusSubscriptions.Store(nfProfile.NfInstanceId, nrfSubData.SubscriptionId) 68 | } 69 | } 70 | 71 | return result, err 72 | } 73 | 74 | func SearchUdmSdmInstance(ue *amf_context.AmfUe, nrfUri string, targetNfType, requestNfType models.NfType, 75 | param *Nnrf_NFDiscovery.SearchNFInstancesParamOpts, 76 | ) error { 77 | resp, localErr := SendSearchNFInstances(nrfUri, targetNfType, requestNfType, param) 78 | if localErr != nil { 79 | return localErr 80 | } 81 | 82 | // select the first UDM_SDM, TODO: select base on other info 83 | var sdmUri string 84 | for _, nfProfile := range resp.NfInstances { 85 | ue.UdmId = nfProfile.NfInstanceId 86 | sdmUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NUDM_SDM, models.NfServiceStatus_REGISTERED) 87 | if sdmUri != "" { 88 | break 89 | } 90 | } 91 | ue.NudmSDMUri = sdmUri 92 | if ue.NudmSDMUri == "" { 93 | err := fmt.Errorf("AMF can not select an UDM by NRF") 94 | logger.ConsumerLog.Errorln(err.Error()) 95 | return err 96 | } 97 | return nil 98 | } 99 | 100 | func SearchNssfNSSelectionInstance(ue *amf_context.AmfUe, nrfUri string, targetNfType, requestNfType models.NfType, 101 | param *Nnrf_NFDiscovery.SearchNFInstancesParamOpts, 102 | ) error { 103 | resp, localErr := SendSearchNFInstances(nrfUri, targetNfType, requestNfType, param) 104 | if localErr != nil { 105 | return localErr 106 | } 107 | 108 | // select the first NSSF, TODO: select base on other info 109 | var nssfUri string 110 | for _, nfProfile := range resp.NfInstances { 111 | ue.NssfId = nfProfile.NfInstanceId 112 | nssfUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NNSSF_NSSELECTION, models.NfServiceStatus_REGISTERED) 113 | if nssfUri != "" { 114 | break 115 | } 116 | } 117 | ue.NssfUri = nssfUri 118 | if ue.NssfUri == "" { 119 | return fmt.Errorf("AMF can not select an NSSF by NRF") 120 | } 121 | return nil 122 | } 123 | 124 | func SearchAmfCommunicationInstance(ue *amf_context.AmfUe, nrfUri string, targetNfType, 125 | requestNfType models.NfType, param *Nnrf_NFDiscovery.SearchNFInstancesParamOpts, 126 | ) (err error) { 127 | resp, localErr := SendSearchNFInstances(nrfUri, targetNfType, requestNfType, param) 128 | if localErr != nil { 129 | err = localErr 130 | return 131 | } 132 | 133 | // select the first AMF, TODO: select base on other info 134 | var amfUri string 135 | for _, nfProfile := range resp.NfInstances { 136 | ue.TargetAmfProfile = &nfProfile 137 | amfUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NAMF_COMM, models.NfServiceStatus_REGISTERED) 138 | if amfUri != "" { 139 | break 140 | } 141 | } 142 | ue.TargetAmfUri = amfUri 143 | if ue.TargetAmfUri == "" { 144 | err = fmt.Errorf("AMF can not select an target AMF by NRF") 145 | } 146 | return 147 | } 148 | -------------------------------------------------------------------------------- /consumer/nsselection.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package consumer 8 | 9 | import ( 10 | "context" 11 | "encoding/json" 12 | "time" 13 | 14 | "github.com/antihax/optional" 15 | amf_context "github.com/omec-project/amf/context" 16 | "github.com/omec-project/amf/logger" 17 | "github.com/omec-project/openapi" 18 | "github.com/omec-project/openapi/Nnssf_NSSelection" 19 | "github.com/omec-project/openapi/models" 20 | ) 21 | 22 | func NSSelectionGetForRegistration(ue *amf_context.AmfUe, requestedNssai []models.MappingOfSnssai) ( 23 | *models.ProblemDetails, error, 24 | ) { 25 | configuration := Nnssf_NSSelection.NewConfiguration() 26 | configuration.SetBasePath(ue.NssfUri) 27 | client := Nnssf_NSSelection.NewAPIClient(configuration) 28 | 29 | amfSelf := amf_context.AMF_Self() 30 | sliceInfo := models.SliceInfoForRegistration{ 31 | SubscribedNssai: ue.SubscribedNssai, 32 | } 33 | 34 | for _, snssai := range requestedNssai { 35 | sliceInfo.RequestedNssai = append(sliceInfo.RequestedNssai, *snssai.ServingSnssai) 36 | if snssai.HomeSnssai != nil { 37 | sliceInfo.MappingOfNssai = append(sliceInfo.MappingOfNssai, snssai) 38 | } 39 | } 40 | 41 | var paramOpt Nnssf_NSSelection.NSSelectionGetParamOpts 42 | if e, err := json.Marshal(sliceInfo); err != nil { 43 | logger.ConsumerLog.Warnf("json marshal failed: %+v", err) 44 | } else { 45 | paramOpt = Nnssf_NSSelection.NSSelectionGetParamOpts{ 46 | SliceInfoRequestForRegistration: optional.NewInterface(string(e)), 47 | } 48 | } 49 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 50 | defer cancel() 51 | res, httpResp, localErr := client.NetworkSliceInformationDocumentApi.NSSelectionGet(ctx, 52 | models.NfType_AMF, amfSelf.NfId, ¶mOpt) 53 | if localErr == nil { 54 | ue.NetworkSliceInfo = &res 55 | for _, allowedNssai := range res.AllowedNssaiList { 56 | ue.AllowedNssai[allowedNssai.AccessType] = allowedNssai.AllowedSnssaiList 57 | } 58 | ue.ConfiguredNssai = res.ConfiguredNssai 59 | } else if httpResp != nil { 60 | if httpResp.Status != localErr.Error() { 61 | err := localErr 62 | return nil, err 63 | } 64 | problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 65 | return &problem, nil 66 | } else { 67 | return nil, openapi.ReportError("NSSF No Response") 68 | } 69 | 70 | return nil, nil 71 | } 72 | 73 | func NSSelectionGetForPduSession(ue *amf_context.AmfUe, snssai models.Snssai) ( 74 | *models.AuthorizedNetworkSliceInfo, *models.ProblemDetails, error, 75 | ) { 76 | configuration := Nnssf_NSSelection.NewConfiguration() 77 | configuration.SetBasePath(ue.NssfUri) 78 | client := Nnssf_NSSelection.NewAPIClient(configuration) 79 | 80 | amfSelf := amf_context.AMF_Self() 81 | sliceInfoForPduSession := models.SliceInfoForPduSession{ 82 | SNssai: &snssai, 83 | RoamingIndication: models.RoamingIndication_NON_ROAMING, // not support roaming 84 | } 85 | 86 | e, err := json.Marshal(sliceInfoForPduSession) 87 | if err != nil { 88 | logger.ConsumerLog.Warnf("json marshal failed: %+v", err) 89 | } 90 | paramOpt := Nnssf_NSSelection.NSSelectionGetParamOpts{ 91 | SliceInfoRequestForPduSession: optional.NewInterface(string(e)), 92 | } 93 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 94 | defer cancel() 95 | res, httpResp, localErr := client.NetworkSliceInformationDocumentApi.NSSelectionGet(ctx, 96 | models.NfType_AMF, amfSelf.NfId, ¶mOpt) 97 | if localErr == nil { 98 | return &res, nil, nil 99 | } else if httpResp != nil { 100 | if httpResp.Status != localErr.Error() { 101 | return nil, nil, localErr 102 | } 103 | problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 104 | return nil, &problem, nil 105 | } else { 106 | return nil, nil, openapi.ReportError("NSSF No Response") 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /consumer/ue_authentication.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package consumer 8 | 9 | import ( 10 | "context" 11 | "encoding/base64" 12 | "fmt" 13 | "net/url" 14 | "strconv" 15 | "time" 16 | 17 | "github.com/antihax/optional" 18 | amf_context "github.com/omec-project/amf/context" 19 | "github.com/omec-project/amf/logger" 20 | "github.com/omec-project/nas/nasType" 21 | "github.com/omec-project/openapi" 22 | "github.com/omec-project/openapi/Nausf_UEAuthentication" 23 | "github.com/omec-project/openapi/models" 24 | ) 25 | 26 | func SendUEAuthenticationAuthenticateRequest(ue *amf_context.AmfUe, 27 | resynchronizationInfo *models.ResynchronizationInfo, 28 | ) (*models.UeAuthenticationCtx, *models.ProblemDetails, error) { 29 | configuration := Nausf_UEAuthentication.NewConfiguration() 30 | configuration.SetBasePath(ue.AusfUri) 31 | 32 | client := Nausf_UEAuthentication.NewAPIClient(configuration) 33 | 34 | amfSelf := amf_context.AMF_Self() 35 | servedGuami := amfSelf.ServedGuamiList[0] 36 | var plmnId *models.PlmnId 37 | // take ServingNetwork plmn from UserLocation.Tai if received 38 | if ue.Tai.PlmnId != nil { 39 | plmnId = ue.Tai.PlmnId 40 | } else { 41 | ue.GmmLog.Warnf("Tai is not received from Serving Network, Serving Plmn [Mcc: %v Mnc: %v] is taken from Guami List", servedGuami.PlmnId.Mcc, servedGuami.PlmnId.Mnc) 42 | plmnId = servedGuami.PlmnId 43 | } 44 | 45 | var authInfo models.AuthenticationInfo 46 | authInfo.SupiOrSuci = ue.Suci 47 | if mnc, err := strconv.Atoi(plmnId.Mnc); err != nil { 48 | return nil, nil, err 49 | } else { 50 | authInfo.ServingNetworkName = fmt.Sprintf("5G:mnc%03d.mcc%s.3gppnetwork.org", mnc, plmnId.Mcc) 51 | } 52 | if resynchronizationInfo != nil { 53 | authInfo.ResynchronizationInfo = resynchronizationInfo 54 | } 55 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 56 | defer cancel() 57 | 58 | ueAuthenticationCtx, httpResponse, err := client.DefaultApi.UeAuthenticationsPost(ctx, authInfo) 59 | if err == nil { 60 | return &ueAuthenticationCtx, nil, nil 61 | } else if httpResponse != nil { 62 | if httpResponse.Status != err.Error() { 63 | return nil, nil, err 64 | } 65 | problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 66 | return nil, &problem, nil 67 | } else { 68 | return nil, nil, openapi.ReportError("server no response") 69 | } 70 | } 71 | 72 | func SendAuth5gAkaConfirmRequest(ue *amf_context.AmfUe, resStar string) ( 73 | *models.ConfirmationDataResponse, *models.ProblemDetails, error, 74 | ) { 75 | var ausfUri string 76 | if confirmUri, err := url.Parse(ue.AuthenticationCtx.Links["link"].Href); err != nil { 77 | return nil, nil, err 78 | } else { 79 | ausfUri = fmt.Sprintf("%s://%s", confirmUri.Scheme, confirmUri.Host) 80 | } 81 | 82 | configuration := Nausf_UEAuthentication.NewConfiguration() 83 | configuration.SetBasePath(ausfUri) 84 | client := Nausf_UEAuthentication.NewAPIClient(configuration) 85 | 86 | confirmData := &Nausf_UEAuthentication.UeAuthenticationsAuthCtxId5gAkaConfirmationPutParamOpts{ 87 | ConfirmationData: optional.NewInterface(models.ConfirmationData{ 88 | ResStar: resStar, 89 | }), 90 | } 91 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 92 | defer cancel() 93 | 94 | confirmResult, httpResponse, err := client.DefaultApi.UeAuthenticationsAuthCtxId5gAkaConfirmationPut( 95 | ctx, ue.Suci, confirmData) 96 | if err == nil { 97 | return &confirmResult, nil, nil 98 | } else if httpResponse != nil { 99 | if httpResponse.Status != err.Error() { 100 | return nil, nil, err 101 | } 102 | switch httpResponse.StatusCode { 103 | case 400, 500: 104 | problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 105 | return nil, &problem, nil 106 | } 107 | return nil, nil, nil 108 | } else { 109 | return nil, nil, openapi.ReportError("server no response") 110 | } 111 | } 112 | 113 | func SendEapAuthConfirmRequest(ue *amf_context.AmfUe, eapMsg nasType.EAPMessage) ( 114 | response *models.EapSession, problemDetails *models.ProblemDetails, err1 error, 115 | ) { 116 | confirmUri, err := url.Parse(ue.AuthenticationCtx.Links["link"].Href) 117 | if err != nil { 118 | logger.ConsumerLog.Errorf("url Parse failed: %+v", err) 119 | } 120 | ausfUri := fmt.Sprintf("%s://%s", confirmUri.Scheme, confirmUri.Host) 121 | 122 | configuration := Nausf_UEAuthentication.NewConfiguration() 123 | configuration.SetBasePath(ausfUri) 124 | client := Nausf_UEAuthentication.NewAPIClient(configuration) 125 | 126 | eapSessionReq := &Nausf_UEAuthentication.EapAuthMethodParamOpts{ 127 | EapSession: optional.NewInterface(models.EapSession{ 128 | EapPayload: base64.StdEncoding.EncodeToString(eapMsg.GetEAPMessage()), 129 | }), 130 | } 131 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 132 | defer cancel() 133 | 134 | eapSession, httpResponse, err := client.DefaultApi.EapAuthMethod(ctx, ue.Suci, eapSessionReq) 135 | if err == nil { 136 | response = &eapSession 137 | } else if httpResponse != nil { 138 | if httpResponse.Status != err.Error() { 139 | err1 = err 140 | return response, problemDetails, err1 141 | } 142 | switch httpResponse.StatusCode { 143 | case 400, 500: 144 | problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 145 | problemDetails = &problem 146 | } 147 | } else { 148 | err1 = openapi.ReportError("server no response") 149 | } 150 | 151 | return response, problemDetails, err1 152 | } 153 | -------------------------------------------------------------------------------- /consumer/ue_context_management.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package consumer 8 | 9 | import ( 10 | "context" 11 | "time" 12 | 13 | amf_context "github.com/omec-project/amf/context" 14 | "github.com/omec-project/openapi" 15 | "github.com/omec-project/openapi/Nudm_UEContextManagement" 16 | "github.com/omec-project/openapi/models" 17 | ) 18 | 19 | func UeCmRegistration(ue *amf_context.AmfUe, accessType models.AccessType, initialRegistrationInd bool) ( 20 | *models.ProblemDetails, error, 21 | ) { 22 | configuration := Nudm_UEContextManagement.NewConfiguration() 23 | configuration.SetBasePath(ue.NudmUECMUri) 24 | client := Nudm_UEContextManagement.NewAPIClient(configuration) 25 | 26 | amfSelf := amf_context.AMF_Self() 27 | 28 | switch accessType { 29 | case models.AccessType__3_GPP_ACCESS: 30 | registrationData := models.Amf3GppAccessRegistration{ 31 | AmfInstanceId: amfSelf.NfId, 32 | InitialRegistrationInd: initialRegistrationInd, 33 | Guami: &amfSelf.ServedGuamiList[0], 34 | RatType: ue.RatType, 35 | // TODO: not support Homogenous Support of IMS Voice over PS Sessions this stage 36 | ImsVoPs: models.ImsVoPs_HOMOGENEOUS_NON_SUPPORT, 37 | } 38 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 39 | defer cancel() 40 | 41 | _, httpResp, localErr := client.AMFRegistrationFor3GPPAccessApi.Registration(ctx, 42 | ue.Supi, registrationData) 43 | if localErr == nil { 44 | return nil, nil 45 | } else if httpResp != nil { 46 | if httpResp.Status != localErr.Error() { 47 | return nil, localErr 48 | } 49 | problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 50 | return &problem, nil 51 | } else { 52 | return nil, openapi.ReportError("server no response") 53 | } 54 | case models.AccessType_NON_3_GPP_ACCESS: 55 | registrationData := models.AmfNon3GppAccessRegistration{ 56 | AmfInstanceId: amfSelf.NfId, 57 | Guami: &amfSelf.ServedGuamiList[0], 58 | RatType: ue.RatType, 59 | } 60 | ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 61 | defer cancel() 62 | 63 | _, httpResp, localErr := client.AMFRegistrationForNon3GPPAccessApi.Register(ctx, ue.Supi, registrationData) 64 | if localErr == nil { 65 | return nil, nil 66 | } else if httpResp != nil { 67 | if httpResp.Status != localErr.Error() { 68 | return nil, localErr 69 | } 70 | problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) 71 | return &problem, nil 72 | } else { 73 | return nil, openapi.ReportError("server no response") 74 | } 75 | } 76 | 77 | return nil, nil 78 | } 79 | -------------------------------------------------------------------------------- /context/3gpp_types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package context 7 | 8 | import ( 9 | "time" 10 | 11 | "github.com/omec-project/openapi/models" 12 | ) 13 | 14 | const ( 15 | MaxNumOfTAI int = 16 16 | MaxNumOfBroadcastPLMNs int = 12 17 | MaxNumOfPLMNs int = 12 18 | MaxNumOfSlice int = 1024 19 | MaxNumOfAllowedSnssais int = 8 20 | MaxValueOfAmfUeNgapId int64 = 1099511627775 21 | MaxNumOfServedGuamiList int = 256 22 | MaxNumOfPDUSessions int = 256 23 | MaxNumOfDRBs int = 32 24 | MaxNumOfAOI int = 64 25 | MaxT3513RetryTimes int = 4 26 | MaxT3522RetryTimes int = 4 27 | MaxT3550RetryTimes int = 4 28 | MaxT3560RetryTimes int = 4 29 | MaxT3565RetryTimes int = 4 30 | MAxNumOfAlgorithm int = 8 31 | DefaultT3502 int = 720 // 12 min 32 | DefaultT3512 int = 3240 // 54 min 33 | DefaultNon3gppDeregistrationTimer int = 3240 // 54 min 34 | ) 35 | 36 | // timers at AMF side, defined in TS 24.501 table 10.2.2 37 | const ( 38 | TimeT3513 time.Duration = 6 * time.Second 39 | TimeT3522 time.Duration = 6 * time.Second 40 | TimeT3550 time.Duration = 6 * time.Second 41 | TimeT3560 time.Duration = 6 * time.Second 42 | TimeT3565 time.Duration = 6 * time.Second 43 | ) 44 | 45 | type LADN struct { 46 | Dnn string 47 | TaiLists []models.Tai 48 | } 49 | 50 | type CauseAll struct { 51 | Cause *models.Cause 52 | NgapCause *models.NgApCause 53 | Var5GmmCause *int32 54 | } 55 | -------------------------------------------------------------------------------- /context/amf_ran.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022-present Intel Corporation 2 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 3 | // Copyright 2019 free5GC.org 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | // 7 | 8 | package context 9 | 10 | import ( 11 | "fmt" 12 | "net" 13 | "strings" 14 | 15 | "github.com/omec-project/amf/factory" 16 | "github.com/omec-project/amf/logger" 17 | "github.com/omec-project/amf/metrics" 18 | "github.com/omec-project/amf/protos/sdcoreAmfServer" 19 | "github.com/omec-project/ngap/ngapConvert" 20 | "github.com/omec-project/ngap/ngapType" 21 | "github.com/omec-project/openapi/models" 22 | mi "github.com/omec-project/util/metricinfo" 23 | "go.uber.org/zap" 24 | ) 25 | 26 | const ( 27 | RanPresentGNbId = 1 28 | RanPresentNgeNbId = 2 29 | RanPresentN3IwfId = 3 30 | RanConnected = "Connected" 31 | RanDisconnected = "Disconnected" 32 | ) 33 | 34 | type AmfRan struct { 35 | RanPresent int 36 | RanId *models.GlobalRanNodeId 37 | Name string 38 | AnType models.AccessType 39 | GnbIp string `json:"-"` // TODO to be removed 40 | GnbId string // RanId in string format, i.e.,mcc:mnc:gnbid 41 | /* socket Connect*/ 42 | Conn net.Conn `json:"-"` 43 | /* Supported TA List */ 44 | SupportedTAList []SupportedTAI // TODO SupportedTaList store and recover from DB 45 | 46 | /* RAN UE List */ 47 | RanUeList []*RanUe `json:"-"` // RanUeNgapId as key 48 | 49 | Amf2RanMsgChan chan *sdcoreAmfServer.AmfMessage `json:"-"` 50 | /* logger */ 51 | Log *zap.SugaredLogger `json:"-"` 52 | } 53 | 54 | type SupportedTAI struct { 55 | Tai models.Tai 56 | SNssaiList []models.Snssai 57 | } 58 | 59 | func NewSupportedTAI() (tai SupportedTAI) { 60 | tai.SNssaiList = make([]models.Snssai, 0, MaxNumOfSlice) 61 | return 62 | } 63 | 64 | func NewSupportedTAIList() []SupportedTAI { 65 | return make([]SupportedTAI, 0, MaxNumOfTAI*MaxNumOfBroadcastPLMNs) 66 | } 67 | 68 | func (ran *AmfRan) Remove() { 69 | // send nf(gnb) status notification 70 | gnbStatus := mi.MetricEvent{ 71 | EventType: mi.CNfStatusEvt, 72 | NfStatusData: mi.CNfStatus{ 73 | NfType: mi.NfTypeGnb, 74 | NfStatus: mi.NfStatusDisconnected, NfName: ran.GnbId, 75 | }, 76 | } 77 | if *factory.AmfConfig.Configuration.KafkaInfo.EnableKafka { 78 | if err := metrics.StatWriter.PublishNfStatusEvent(gnbStatus); err != nil { 79 | ran.Log.Errorf("could not publish NfStatusEvent: %v", err) 80 | } 81 | } 82 | 83 | ran.SetRanStats(RanDisconnected) 84 | ran.Log.Infof("remove RAN Context[ID: %+v]", ran.RanID()) 85 | ran.RemoveAllUeInRan() 86 | if AMF_Self().EnableSctpLb { 87 | if ran.GnbId != "" { 88 | AMF_Self().DeleteAmfRanId(ran.GnbId) 89 | } 90 | } else { 91 | AMF_Self().DeleteAmfRan(ran.Conn) 92 | } 93 | } 94 | 95 | func (ran *AmfRan) NewRanUe(ranUeNgapID int64) (*RanUe, error) { 96 | ranUe := RanUe{} 97 | self := AMF_Self() 98 | amfUeNgapID, err := self.AllocateAmfUeNgapID() 99 | if err != nil { 100 | ran.Log.Errorln("alloc Amf ue ngap id failed", err) 101 | return nil, fmt.Errorf("allocate AMF UE NGAP ID error: %+v", err) 102 | } 103 | ranUe.AmfUeNgapId = amfUeNgapID 104 | ranUe.RanUeNgapId = ranUeNgapID 105 | ranUe.Ran = ran 106 | ranUe.Log = ran.Log.With(logger.FieldAmfUeNgapID, fmt.Sprintf("AMF_UE_NGAP_ID:%d", ranUe.AmfUeNgapId)) 107 | ran.RanUeList = append(ran.RanUeList, &ranUe) 108 | self.RanUePool.Store(ranUe.AmfUeNgapId, &ranUe) 109 | return &ranUe, nil 110 | } 111 | 112 | func (ran *AmfRan) RemoveAllUeInRan() { 113 | for _, ranUe := range ran.RanUeList { 114 | if err := ranUe.Remove(); err != nil { 115 | logger.ContextLog.Errorf("Remove RanUe error: %v", err) 116 | } 117 | } 118 | } 119 | 120 | func (ran *AmfRan) RanUeFindByRanUeNgapIDLocal(ranUeNgapID int64) *RanUe { 121 | // TODO - need fix..Make this map so search is fast 122 | for _, ranUe := range ran.RanUeList { 123 | if ranUe.RanUeNgapId == ranUeNgapID { 124 | return ranUe 125 | } 126 | } 127 | ran.Log.Infof("RanUe does not exist") 128 | return nil 129 | } 130 | 131 | func (ran *AmfRan) RanUeFindByRanUeNgapID(ranUeNgapID int64) *RanUe { 132 | ranUe := ran.RanUeFindByRanUeNgapIDLocal(ranUeNgapID) 133 | 134 | if ranUe != nil { 135 | return ranUe 136 | } 137 | 138 | if AMF_Self().EnableDbStore { 139 | ranUe := DbFetchRanUeByRanUeNgapID(ranUeNgapID, ran) 140 | if ranUe != nil { 141 | ranUe.Ran = ran 142 | ran.RanUeList = append(ran.RanUeList, ranUe) 143 | return ranUe 144 | } 145 | } 146 | 147 | return nil 148 | } 149 | 150 | func (ran *AmfRan) SetRanId(ranNodeId *ngapType.GlobalRANNodeID) { 151 | ranId := ngapConvert.RanIdToModels(*ranNodeId) 152 | ran.RanPresent = ranNodeId.Present 153 | ran.RanId = &ranId 154 | if ranNodeId.Present == ngapType.GlobalRANNodeIDPresentGlobalN3IWFID { 155 | ran.AnType = models.AccessType_NON_3_GPP_ACCESS 156 | } else { 157 | ran.AnType = models.AccessType__3_GPP_ACCESS 158 | } 159 | 160 | // Setting RanId in String format with ":" separation of each field 161 | if ranId.PlmnId != nil { 162 | ran.GnbId = ranId.PlmnId.Mcc + ":" + ranId.PlmnId.Mnc + ":" 163 | } 164 | if ranId.GNbId != nil { 165 | ran.GnbId += ranId.GNbId.GNBValue 166 | } 167 | } 168 | 169 | func (ran *AmfRan) ConvertGnbIdToRanId(gnbId string) (ranNodeId *models.GlobalRanNodeId) { 170 | ranId := &models.GlobalRanNodeId{} 171 | val := strings.Split(gnbId, ":") 172 | if len(val) != 3 { 173 | return nil 174 | } 175 | ranId.PlmnId = &models.PlmnId{Mcc: val[0], Mnc: val[1]} 176 | ranId.GNbId = &models.GNbId{GNBValue: val[2]} 177 | ran.RanPresent = RanPresentGNbId 178 | return ranId 179 | } 180 | 181 | func (ran *AmfRan) RanID() string { 182 | switch ran.RanPresent { 183 | case RanPresentGNbId: 184 | return fmt.Sprintf("", *ran.RanId.PlmnId, ran.RanId.GNbId.GNBValue) 185 | case RanPresentN3IwfId: 186 | return fmt.Sprintf("", *ran.RanId.PlmnId, ran.RanId.N3IwfId) 187 | case RanPresentNgeNbId: 188 | return fmt.Sprintf("", *ran.RanId.PlmnId, ran.RanId.NgeNbId) 189 | default: 190 | return "" 191 | } 192 | } 193 | 194 | func (ran *AmfRan) SetRanStats(state string) { 195 | for _, tai := range ran.SupportedTAList { 196 | if state == RanConnected { 197 | metrics.SetGnbSessProfileStats(ran.Name, ran.GnbIp, state, tai.Tai.Tac, 1) 198 | } else { 199 | metrics.SetGnbSessProfileStats(ran.Name, ran.GnbIp, state, tai.Tai.Tac, 0) 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /context/common_function.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package context 7 | 8 | import ( 9 | "reflect" 10 | 11 | "github.com/mohae/deepcopy" 12 | "github.com/omec-project/amf/logger" 13 | "github.com/omec-project/openapi/models" 14 | ) 15 | 16 | func CompareUserLocation(loc1 models.UserLocation, loc2 models.UserLocation) bool { 17 | if loc1.EutraLocation != nil && loc2.EutraLocation != nil { 18 | eutraloc1 := deepcopy.Copy(*loc1.EutraLocation).(models.EutraLocation) 19 | eutraloc2 := deepcopy.Copy(*loc2.EutraLocation).(models.EutraLocation) 20 | eutraloc1.UeLocationTimestamp = nil 21 | eutraloc2.UeLocationTimestamp = nil 22 | return reflect.DeepEqual(eutraloc1, eutraloc2) 23 | } 24 | if loc1.N3gaLocation != nil && loc2.N3gaLocation != nil { 25 | return reflect.DeepEqual(loc1, loc2) 26 | } 27 | if loc1.NrLocation != nil && loc2.NrLocation != nil { 28 | nrloc1 := deepcopy.Copy(*loc1.NrLocation).(models.NrLocation) 29 | nrloc2 := deepcopy.Copy(*loc2.NrLocation).(models.NrLocation) 30 | nrloc1.UeLocationTimestamp = nil 31 | nrloc2.UeLocationTimestamp = nil 32 | return reflect.DeepEqual(nrloc1, nrloc2) 33 | } 34 | 35 | return false 36 | } 37 | 38 | func InTaiList(servedTai models.Tai, taiList []models.Tai) bool { 39 | for _, tai := range taiList { 40 | if reflect.DeepEqual(tai, servedTai) { 41 | return true 42 | } 43 | } 44 | return false 45 | } 46 | 47 | func IsTaiEqual(servedTai models.Tai, targetTai models.Tai) bool { 48 | return servedTai.PlmnId.Mcc == targetTai.PlmnId.Mcc && servedTai.PlmnId.Mnc == targetTai.PlmnId.Mnc && servedTai.Tac == targetTai.Tac 49 | } 50 | 51 | func TacInAreas(targetTac string, areas []models.Area) bool { 52 | for _, area := range areas { 53 | for _, tac := range area.Tacs { 54 | if targetTac == tac { 55 | return true 56 | } 57 | } 58 | } 59 | return false 60 | } 61 | 62 | func AttachSourceUeTargetUe(sourceUe, targetUe *RanUe) { 63 | if sourceUe == nil { 64 | logger.ContextLog.Error("Source Ue is Nil") 65 | return 66 | } 67 | if targetUe == nil { 68 | logger.ContextLog.Error("Target Ue is Nil") 69 | return 70 | } 71 | amfUe := sourceUe.AmfUe 72 | if amfUe == nil { 73 | logger.ContextLog.Error("AmfUe is Nil") 74 | return 75 | } 76 | targetUe.AmfUe = amfUe 77 | targetUe.SourceUe = sourceUe 78 | sourceUe.TargetUe = targetUe 79 | } 80 | 81 | func DetachSourceUeTargetUe(ranUe *RanUe) { 82 | if ranUe == nil { 83 | logger.ContextLog.Error("ranUe is Nil") 84 | return 85 | } 86 | if ranUe.TargetUe != nil { 87 | targetUe := ranUe.TargetUe 88 | 89 | ranUe.TargetUe = nil 90 | targetUe.SourceUe = nil 91 | } else if ranUe.SourceUe != nil { 92 | source := ranUe.SourceUe 93 | 94 | ranUe.SourceUe = nil 95 | source.TargetUe = nil 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /context/sm_context.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022-present Intel Corporation 2 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 3 | // Copyright 2019 free5GC.org 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | // 7 | 8 | package context 9 | 10 | import ( 11 | "sync" 12 | 13 | "github.com/omec-project/nas/nasMessage" 14 | "github.com/omec-project/openapi/models" 15 | ) 16 | 17 | type SmContext struct { 18 | Mu *sync.RWMutex // protect the following fields 19 | 20 | // pdu session information 21 | PduSessionIDVal int32 22 | SmContextRefVal string 23 | SnssaiVal models.Snssai 24 | DnnVal string 25 | AccessTypeVal models.AccessType 26 | NsInstanceVal string 27 | UserLocationVal models.UserLocation 28 | PlmnIDVal models.PlmnId 29 | 30 | // SMF information 31 | SmfIDVal string 32 | SmfUriVal string 33 | HSmfIDVal string 34 | VSmfIDVal string 35 | 36 | // status of pdusession 37 | PduSessionInactiveVal bool 38 | 39 | // for duplicate pdu session id handling 40 | UlNASTransportVal *nasMessage.ULNASTransport 41 | DuplicatedVal bool 42 | 43 | SmfProfiles []models.NfProfile 44 | } 45 | 46 | func NewSmContext(pduSessionID int32) *SmContext { 47 | c := &SmContext{ 48 | PduSessionIDVal: pduSessionID, 49 | Mu: new(sync.RWMutex), 50 | } 51 | return c 52 | } 53 | 54 | func (c *SmContext) IsPduSessionActive() bool { 55 | return !c.PduSessionInactiveVal 56 | } 57 | 58 | func (c *SmContext) SetPduSessionInActive(s bool) { 59 | c.PduSessionInactiveVal = s 60 | } 61 | 62 | func (c *SmContext) PduSessionID() int32 { 63 | c.Mu.RLock() 64 | defer c.Mu.RUnlock() 65 | return c.PduSessionIDVal 66 | } 67 | 68 | func (c *SmContext) SetPduSessionID(id int32) { 69 | c.Mu.Lock() 70 | defer c.Mu.Unlock() 71 | c.PduSessionIDVal = id 72 | } 73 | 74 | func (c *SmContext) SmContextRef() string { 75 | c.Mu.RLock() 76 | defer c.Mu.RUnlock() 77 | return c.SmContextRefVal 78 | } 79 | 80 | func (c *SmContext) SetSmContextRef(ref string) { 81 | c.Mu.Lock() 82 | defer c.Mu.Unlock() 83 | c.SmContextRefVal = ref 84 | } 85 | 86 | func (c *SmContext) AccessType() models.AccessType { 87 | c.Mu.RLock() 88 | defer c.Mu.RUnlock() 89 | return c.AccessTypeVal 90 | } 91 | 92 | func (c *SmContext) SetAccessType(accessType models.AccessType) { 93 | c.Mu.Lock() 94 | defer c.Mu.Unlock() 95 | c.AccessTypeVal = accessType 96 | } 97 | 98 | func (c *SmContext) Snssai() models.Snssai { 99 | c.Mu.RLock() 100 | defer c.Mu.RUnlock() 101 | return c.SnssaiVal 102 | } 103 | 104 | func (c *SmContext) SetSnssai(snssai models.Snssai) { 105 | c.Mu.Lock() 106 | defer c.Mu.Unlock() 107 | c.SnssaiVal = snssai 108 | } 109 | 110 | func (c *SmContext) Dnn() string { 111 | c.Mu.RLock() 112 | defer c.Mu.RUnlock() 113 | return c.DnnVal 114 | } 115 | 116 | func (c *SmContext) SetDnn(dnn string) { 117 | c.Mu.Lock() 118 | defer c.Mu.Unlock() 119 | c.DnnVal = dnn 120 | } 121 | 122 | func (c *SmContext) NsInstance() string { 123 | c.Mu.RLock() 124 | defer c.Mu.RUnlock() 125 | return c.NsInstanceVal 126 | } 127 | 128 | func (c *SmContext) SetNsInstance(nsInstanceID string) { 129 | c.Mu.Lock() 130 | defer c.Mu.Unlock() 131 | c.NsInstanceVal = nsInstanceID 132 | } 133 | 134 | func (c *SmContext) UserLocation() models.UserLocation { 135 | c.Mu.RLock() 136 | defer c.Mu.RUnlock() 137 | return c.UserLocationVal 138 | } 139 | 140 | func (c *SmContext) SetUserLocation(userLocation models.UserLocation) { 141 | c.Mu.Lock() 142 | defer c.Mu.Unlock() 143 | c.UserLocationVal = userLocation 144 | } 145 | 146 | func (c *SmContext) PlmnID() models.PlmnId { 147 | c.Mu.RLock() 148 | defer c.Mu.RUnlock() 149 | return c.PlmnIDVal 150 | } 151 | 152 | func (c *SmContext) SetPlmnID(plmnID models.PlmnId) { 153 | c.Mu.Lock() 154 | defer c.Mu.Unlock() 155 | c.PlmnIDVal = plmnID 156 | } 157 | 158 | func (c *SmContext) SmfID() string { 159 | c.Mu.RLock() 160 | defer c.Mu.RUnlock() 161 | return c.SmfIDVal 162 | } 163 | 164 | func (c *SmContext) SetSmfID(smfID string) { 165 | c.Mu.Lock() 166 | defer c.Mu.Unlock() 167 | c.SmfIDVal = smfID 168 | } 169 | 170 | func (c *SmContext) SmfUri() string { 171 | c.Mu.RLock() 172 | defer c.Mu.RUnlock() 173 | return c.SmfUriVal 174 | } 175 | 176 | func (c *SmContext) SetSmfUri(smfUri string) { 177 | c.Mu.Lock() 178 | defer c.Mu.Unlock() 179 | c.SmfUriVal = smfUri 180 | } 181 | 182 | func (c *SmContext) HSmfID() string { 183 | c.Mu.RLock() 184 | defer c.Mu.RUnlock() 185 | return c.HSmfIDVal 186 | } 187 | 188 | func (c *SmContext) SetHSmfID(hsmfID string) { 189 | c.Mu.Lock() 190 | defer c.Mu.Unlock() 191 | c.HSmfIDVal = hsmfID 192 | } 193 | 194 | func (c *SmContext) VSmfID() string { 195 | c.Mu.RLock() 196 | defer c.Mu.RUnlock() 197 | return c.VSmfIDVal 198 | } 199 | 200 | func (c *SmContext) SetVSmfID(vsmfID string) { 201 | c.Mu.Lock() 202 | defer c.Mu.Unlock() 203 | c.VSmfIDVal = vsmfID 204 | } 205 | 206 | func (c *SmContext) PduSessionIDDuplicated() bool { 207 | c.Mu.RLock() 208 | defer c.Mu.RUnlock() 209 | return c.DuplicatedVal 210 | } 211 | 212 | func (c *SmContext) SetDuplicatedPduSessionID(duplicated bool) { 213 | c.Mu.Lock() 214 | defer c.Mu.Unlock() 215 | c.DuplicatedVal = duplicated 216 | } 217 | 218 | func (c *SmContext) ULNASTransport() *nasMessage.ULNASTransport { 219 | c.Mu.RLock() 220 | defer c.Mu.RUnlock() 221 | return c.UlNASTransportVal 222 | } 223 | 224 | func (c *SmContext) StoreULNASTransport(msg *nasMessage.ULNASTransport) { 225 | c.Mu.Lock() 226 | defer c.Mu.Unlock() 227 | c.UlNASTransportVal = msg 228 | } 229 | 230 | func (c *SmContext) DeleteULNASTransport() { 231 | c.Mu.Lock() 232 | defer c.Mu.Unlock() 233 | c.UlNASTransportVal = nil 234 | } 235 | -------------------------------------------------------------------------------- /context/timer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package context 7 | 8 | import ( 9 | "sync/atomic" 10 | "time" 11 | ) 12 | 13 | // Timer can be used for retransmission, it will manage retry times automatically 14 | type Timer struct { 15 | ticker *time.Ticker 16 | expireTimes int32 // accessed atomically 17 | maxRetryTimes int32 // accessed atomically 18 | done chan bool 19 | } 20 | 21 | // NewTimer will return a Timer struct and create a goroutine. Then it calls expiredFunc every time interval d until 22 | // the user call Stop(). the number of expire event is be recorded when the timer is active. When the number of expire 23 | // event is > maxRetryTimes, then the timer will call cancelFunc and turns off itself. Whether expiredFunc pass a 24 | // parameter expireTimes to tell the user that the current expireTimes. 25 | func NewTimer(d time.Duration, maxRetryTimes int, 26 | expiredFunc func(expireTimes int32), 27 | cancelFunc func(), 28 | ) *Timer { 29 | t := &Timer{} 30 | atomic.StoreInt32(&t.expireTimes, 0) 31 | atomic.StoreInt32(&t.maxRetryTimes, int32(maxRetryTimes)) 32 | t.done = make(chan bool, 1) 33 | t.ticker = time.NewTicker(d) 34 | 35 | go func(ticker *time.Ticker) { 36 | defer ticker.Stop() 37 | 38 | for { 39 | select { 40 | case <-t.done: 41 | return 42 | case <-ticker.C: 43 | atomic.AddInt32(&t.expireTimes, 1) 44 | if t.ExpireTimes() > t.MaxRetryTimes() { 45 | cancelFunc() 46 | return 47 | } else { 48 | expiredFunc(t.ExpireTimes()) 49 | } 50 | } 51 | } 52 | }(t.ticker) 53 | 54 | return t 55 | } 56 | 57 | // MaxRetryTimes return the max retry times of the timer 58 | func (t *Timer) MaxRetryTimes() int32 { 59 | return atomic.LoadInt32(&t.maxRetryTimes) 60 | } 61 | 62 | // ExpireTimes return the current expire times of the timer 63 | func (t *Timer) ExpireTimes() int32 { 64 | return atomic.LoadInt32(&t.expireTimes) 65 | } 66 | 67 | // Stop turns off the timer, after Stop, no more timeout event will be triggered. User should call Stop() only once 68 | // otherwise it may hang on writing to done channel 69 | func (t *Timer) Stop() { 70 | t.done <- true 71 | close(t.done) 72 | } 73 | -------------------------------------------------------------------------------- /context/transaction.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package context 8 | 9 | type EventChannel struct { 10 | Message chan interface{} 11 | Event chan string 12 | AmfUe *AmfUe 13 | NasHandler func(*AmfUe, NasMsg) 14 | NgapHandler func(*AmfUe, NgapMsg) 15 | SbiHandler func(s1, s2 string, msg interface{}) (interface{}, string, interface{}, interface{}) 16 | ConfigHandler func(s1, s2, s3 string, msg interface{}) 17 | } 18 | 19 | func (tx *EventChannel) UpdateNgapHandler(handler func(*AmfUe, NgapMsg)) { 20 | tx.AmfUe.TxLog.Infof("updated ngaphandler") 21 | tx.NgapHandler = handler 22 | } 23 | 24 | func (tx *EventChannel) UpdateNasHandler(handler func(*AmfUe, NasMsg)) { 25 | tx.AmfUe.TxLog.Infof("updated nashandler") 26 | tx.NasHandler = handler 27 | } 28 | 29 | func (tx *EventChannel) UpdateSbiHandler(handler func(s1, s2 string, msg interface{}) (interface{}, string, interface{}, interface{})) { 30 | tx.AmfUe.TxLog.Infof("updated sbihandler") 31 | tx.SbiHandler = handler 32 | } 33 | 34 | func (tx *EventChannel) UpdateConfigHandler(handler func(s1, s2, s3 string, msg interface{})) { 35 | tx.AmfUe.TxLog.Infof("updated confighandler") 36 | tx.ConfigHandler = handler 37 | } 38 | 39 | func (tx *EventChannel) Start() { 40 | for { 41 | select { 42 | case msg := <-tx.Message: 43 | switch msg := msg.(type) { 44 | case NasMsg: 45 | tx.NasHandler(tx.AmfUe, msg) 46 | case NgapMsg: 47 | tx.NgapHandler(tx.AmfUe, msg) 48 | case SbiMsg: 49 | p_1, p_2, p_3, p_4 := tx.SbiHandler(msg.UeContextId, msg.ReqUri, msg.Msg) 50 | res := SbiResponseMsg{ 51 | RespData: p_1, 52 | LocationHeader: p_2, 53 | ProblemDetails: p_3, 54 | TransferErr: p_4, 55 | } 56 | msg.Result <- res 57 | case ConfigMsg: 58 | tx.ConfigHandler(msg.Supi, msg.Sst, msg.Sd, msg.Msg) 59 | } 60 | case event := <-tx.Event: 61 | if event == "quit" { 62 | tx.AmfUe.TxLog.Infof("closed ue goroutine") 63 | return 64 | } 65 | } 66 | } 67 | } 68 | 69 | func (tx *EventChannel) SubmitMessage(msg interface{}) { 70 | tx.Message <- msg 71 | } 72 | -------------------------------------------------------------------------------- /docs/images/README-AMF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omec-project/amf/c84dcd85a8443e0c6b6a7629d9fc62df577f9e5b/docs/images/README-AMF.png -------------------------------------------------------------------------------- /docs/images/README-AMF.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2022 Open Networking Foundation 2 | 3 | SPDX-License-Identifier: Apache-2.0 4 | -------------------------------------------------------------------------------- /eventexposure/api_individual_subscription_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_EventExposure 8 | * 9 | * AMF Event Exposure Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package eventexposure 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | "github.com/omec-project/amf/producer" 23 | "github.com/omec-project/openapi" 24 | "github.com/omec-project/openapi/models" 25 | "github.com/omec-project/util/httpwrapper" 26 | ) 27 | 28 | // DeleteSubscription - Namf_EventExposure Unsubscribe service Operation 29 | func HTTPDeleteSubscription(c *gin.Context) { 30 | req := httpwrapper.NewRequest(c.Request, nil) 31 | req.Params["subscriptionId"] = c.Param("subscriptionId") 32 | 33 | rsp := producer.HandleDeleteAMFEventSubscription(req) 34 | 35 | if rsp.Status == http.StatusOK { 36 | c.JSON(http.StatusOK, gin.H{}) 37 | } else { 38 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 39 | if err != nil { 40 | logger.EeLog.Errorln(err) 41 | problemDetails := models.ProblemDetails{ 42 | Status: http.StatusInternalServerError, 43 | Cause: "SYSTEM_FAILURE", 44 | Detail: err.Error(), 45 | } 46 | c.JSON(http.StatusInternalServerError, problemDetails) 47 | } else { 48 | c.Data(rsp.Status, "application/json", responseBody) 49 | } 50 | } 51 | } 52 | 53 | // ModifySubscription - Namf_EventExposure Subscribe Modify service Operation 54 | func HTTPModifySubscription(c *gin.Context) { 55 | var modifySubscriptionRequest models.ModifySubscriptionRequest 56 | 57 | requestBody, err := c.GetRawData() 58 | if err != nil { 59 | logger.EeLog.Errorf("Get Request Body error: %+v", err) 60 | problemDetail := models.ProblemDetails{ 61 | Title: "System failure", 62 | Status: http.StatusInternalServerError, 63 | Detail: err.Error(), 64 | Cause: "SYSTEM_FAILURE", 65 | } 66 | c.JSON(http.StatusInternalServerError, problemDetail) 67 | return 68 | } 69 | 70 | err = openapi.Deserialize(&modifySubscriptionRequest, requestBody, "application/json") 71 | if err != nil { 72 | problemDetail := "[Request Body] " + err.Error() 73 | rsp := models.ProblemDetails{ 74 | Title: "Malformed request syntax", 75 | Status: http.StatusBadRequest, 76 | Detail: problemDetail, 77 | } 78 | logger.EeLog.Errorln(problemDetail) 79 | c.JSON(http.StatusBadRequest, rsp) 80 | return 81 | } 82 | 83 | req := httpwrapper.NewRequest(c.Request, modifySubscriptionRequest) 84 | req.Params["subscriptionId"] = c.Param("subscriptionId") 85 | 86 | rsp := producer.HandleModifyAMFEventSubscription(req) 87 | 88 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 89 | if err != nil { 90 | logger.EeLog.Errorln(err) 91 | problemDetails := models.ProblemDetails{ 92 | Status: http.StatusInternalServerError, 93 | Cause: "SYSTEM_FAILURE", 94 | Detail: err.Error(), 95 | } 96 | c.JSON(http.StatusInternalServerError, problemDetails) 97 | } else { 98 | c.Data(rsp.Status, "application/json", responseBody) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /eventexposure/api_subscriptions_collection_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_EventExposure 8 | * 9 | * AMF Event Exposure Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package eventexposure 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | "github.com/omec-project/amf/producer" 23 | "github.com/omec-project/openapi" 24 | "github.com/omec-project/openapi/models" 25 | "github.com/omec-project/util/httpwrapper" 26 | ) 27 | 28 | // CreateSubscription - Namf_EventExposure Subscribe service Operation 29 | func HTTPCreateSubscription(c *gin.Context) { 30 | var createEventSubscription models.AmfCreateEventSubscription 31 | 32 | requestBody, err := c.GetRawData() 33 | if err != nil { 34 | logger.EeLog.Errorf("Get Request Body error: %+v", err) 35 | problemDetail := models.ProblemDetails{ 36 | Title: "System failure", 37 | Status: http.StatusInternalServerError, 38 | Detail: err.Error(), 39 | Cause: "SYSTEM_FAILURE", 40 | } 41 | c.JSON(http.StatusInternalServerError, problemDetail) 42 | return 43 | } 44 | 45 | err = openapi.Deserialize(&createEventSubscription, requestBody, "application/json") 46 | if err != nil { 47 | problemDetail := "[Request Body] " + err.Error() 48 | rsp := models.ProblemDetails{ 49 | Title: "Malformed request syntax", 50 | Status: http.StatusBadRequest, 51 | Detail: problemDetail, 52 | } 53 | logger.EeLog.Errorln(problemDetail) 54 | c.JSON(http.StatusBadRequest, rsp) 55 | return 56 | } 57 | 58 | req := httpwrapper.NewRequest(c.Request, createEventSubscription) 59 | 60 | rsp := producer.HandleCreateAMFEventSubscription(req) 61 | 62 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 63 | if err != nil { 64 | logger.EeLog.Errorln(err) 65 | problemDetails := models.ProblemDetails{ 66 | Status: http.StatusInternalServerError, 67 | Cause: "SYSTEM_FAILURE", 68 | Detail: err.Error(), 69 | } 70 | c.JSON(http.StatusInternalServerError, problemDetails) 71 | } else { 72 | c.Data(rsp.Status, "application/json", responseBody) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /eventexposure/routers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_EventExposure 8 | * 9 | * AMF Event Exposure Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package eventexposure 16 | 17 | import ( 18 | "net/http" 19 | "strings" 20 | 21 | "github.com/gin-gonic/gin" 22 | "github.com/omec-project/amf/logger" 23 | utilLogger "github.com/omec-project/util/logger" 24 | ) 25 | 26 | // Route is the information for every URI. 27 | type Route struct { 28 | // Name is the name of this Route. 29 | Name string 30 | // Method is the string for the HTTP method. ex) GET, POST etc.. 31 | Method string 32 | // Pattern is the pattern of the URI. 33 | Pattern string 34 | // HandlerFunc is the handler function of this route. 35 | HandlerFunc gin.HandlerFunc 36 | } 37 | 38 | // Routes is the list of the generated Route. 39 | type Routes []Route 40 | 41 | // NewRouter returns a new router. 42 | func NewRouter() *gin.Engine { 43 | router := utilLogger.NewGinWithZap(logger.GinLog) 44 | AddService(router) 45 | return router 46 | } 47 | 48 | func AddService(engine *gin.Engine) *gin.RouterGroup { 49 | group := engine.Group("/namf-evts/v1") 50 | 51 | for _, route := range routes { 52 | switch route.Method { 53 | case "GET": 54 | group.GET(route.Pattern, route.HandlerFunc) 55 | case "POST": 56 | group.POST(route.Pattern, route.HandlerFunc) 57 | case "PUT": 58 | group.PUT(route.Pattern, route.HandlerFunc) 59 | case "DELETE": 60 | group.DELETE(route.Pattern, route.HandlerFunc) 61 | case "PATCH": 62 | group.PATCH(route.Pattern, route.HandlerFunc) 63 | } 64 | } 65 | return group 66 | } 67 | 68 | // Index is the index handler. 69 | func Index(c *gin.Context) { 70 | c.String(http.StatusOK, "Hello World!") 71 | } 72 | 73 | var routes = Routes{ 74 | { 75 | "Index", 76 | "GET", 77 | "/", 78 | Index, 79 | }, 80 | 81 | { 82 | "HTTPDeleteSubscription", 83 | strings.ToUpper("Delete"), 84 | "/subscriptions/:subscriptionId", 85 | HTTPDeleteSubscription, 86 | }, 87 | 88 | { 89 | "HTTPModifySubscription", 90 | strings.ToUpper("Patch"), 91 | "/subscriptions/:subscriptionId", 92 | HTTPModifySubscription, 93 | }, 94 | 95 | { 96 | "HTTPCreateSubscription", 97 | strings.ToUpper("Post"), 98 | "/subscriptions", 99 | HTTPCreateSubscription, 100 | }, 101 | } 102 | -------------------------------------------------------------------------------- /factory/amf_config_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // SPDX-FileCopyrightText: 2024 Canonical Ltd. 3 | /* 4 | * Tests for AMF Configuration Factory 5 | */ 6 | 7 | package factory 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | // Webui URL is not set then default Webui URL value is returned 16 | func TestGetDefaultWebuiUrl(t *testing.T) { 17 | if err := InitConfigFactory("../amfTest/amfcfg.yaml"); err != nil { 18 | t.Logf("Error in InitConfigFactory: %v", err) 19 | } 20 | got := AmfConfig.Configuration.WebuiUri 21 | want := "webui:9876" 22 | assert.Equal(t, got, want, "The webui URL is not correct.") 23 | } 24 | 25 | // Webui URL is set to a custom value then custom Webui URL is returned 26 | func TestGetCustomWebuiUrl(t *testing.T) { 27 | if err := InitConfigFactory("../amfTest/amfcfg_with_custom_webui_url.yaml"); err != nil { 28 | t.Logf("Error in InitConfigFactory: %v", err) 29 | } 30 | got := AmfConfig.Configuration.WebuiUri 31 | want := "myspecialwebui:9872" 32 | assert.Equal(t, got, want, "The webui URL is not correct.") 33 | } 34 | -------------------------------------------------------------------------------- /factory/factory.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | /* 8 | * AMF Configuration Factory 9 | */ 10 | 11 | package factory 12 | 13 | import ( 14 | "fmt" 15 | "os" 16 | "reflect" 17 | 18 | "github.com/omec-project/amf/logger" 19 | "gopkg.in/yaml.v2" 20 | ) 21 | 22 | var AmfConfig Config 23 | 24 | // TODO: Support configuration update from REST api 25 | func InitConfigFactory(f string) error { 26 | if content, err := os.ReadFile(f); err != nil { 27 | return err 28 | } else { 29 | AmfConfig = Config{} 30 | 31 | if yamlErr := yaml.Unmarshal(content, &AmfConfig); yamlErr != nil { 32 | return yamlErr 33 | } 34 | if AmfConfig.Configuration.WebuiUri == "" { 35 | AmfConfig.Configuration.WebuiUri = "webui:9876" 36 | } 37 | if AmfConfig.Configuration.KafkaInfo.EnableKafka == nil { 38 | enableKafka := true 39 | AmfConfig.Configuration.KafkaInfo.EnableKafka = &enableKafka 40 | } 41 | } 42 | 43 | return nil 44 | } 45 | 46 | func UpdateConfig(f string) error { 47 | if content, err := os.ReadFile(f); err != nil { 48 | return err 49 | } else { 50 | var amfConfig Config 51 | 52 | if yamlErr := yaml.Unmarshal(content, &amfConfig); yamlErr != nil { 53 | return yamlErr 54 | } 55 | // Checking which config has been changed 56 | if !reflect.DeepEqual(AmfConfig.Configuration.AmfName, amfConfig.Configuration.AmfName) { 57 | logger.CfgLog.Infoln("updated AMF Name is changed to:", amfConfig.Configuration.AmfName) 58 | } 59 | if !reflect.DeepEqual(AmfConfig.Configuration.NgapIpList, amfConfig.Configuration.NgapIpList) { 60 | logger.CfgLog.Infoln("updated NgapList:", amfConfig.Configuration.NgapIpList) 61 | } 62 | if !reflect.DeepEqual(AmfConfig.Configuration.Sbi, amfConfig.Configuration.Sbi) { 63 | logger.CfgLog.Infoln("updated Sbi:", amfConfig.Configuration.Sbi) 64 | } 65 | if !reflect.DeepEqual(AmfConfig.Configuration.NetworkFeatureSupport5GS, amfConfig.Configuration.NetworkFeatureSupport5GS) { 66 | logger.CfgLog.Infoln("updated NetworkFeatureSupport5GS:", amfConfig.Configuration.NetworkFeatureSupport5GS) 67 | } 68 | if !reflect.DeepEqual(AmfConfig.Configuration.ServiceNameList, amfConfig.Configuration.ServiceNameList) { 69 | logger.CfgLog.Infoln("updated ServiceNameList:", amfConfig.Configuration.ServiceNameList) 70 | } 71 | 72 | /* we will not update below 3 configs if its controlled by ROC */ 73 | /* TODO: document this as dynamic configmap updates for below 3 configs we dont support if its controlled by ROC*/ 74 | if os.Getenv("MANAGED_BY_CONFIG_POD") == "true" { 75 | if !reflect.DeepEqual(AmfConfig.Configuration.ServedGumaiList, amfConfig.Configuration.ServedGumaiList) { 76 | logger.CfgLog.Infoln("updated ServedGumaiList:", amfConfig.Configuration.ServedGumaiList) 77 | } 78 | if !reflect.DeepEqual(AmfConfig.Configuration.SupportTAIList, amfConfig.Configuration.SupportTAIList) { 79 | logger.CfgLog.Infoln("updated SupportTAIList:", amfConfig.Configuration.SupportTAIList) 80 | } 81 | if !reflect.DeepEqual(AmfConfig.Configuration.PlmnSupportList, amfConfig.Configuration.PlmnSupportList) { 82 | logger.CfgLog.Infoln("updated PlmnSupportList:", amfConfig.Configuration.PlmnSupportList) 83 | } 84 | } 85 | if !reflect.DeepEqual(AmfConfig.Configuration.SupportDnnList, amfConfig.Configuration.SupportDnnList) { 86 | logger.CfgLog.Infoln("updated SupportDnnList:", amfConfig.Configuration.SupportDnnList) 87 | } 88 | if !reflect.DeepEqual(AmfConfig.Configuration.NrfUri, amfConfig.Configuration.NrfUri) { 89 | logger.CfgLog.Infoln("updated NrfUri:", amfConfig.Configuration.NrfUri) 90 | } 91 | if !reflect.DeepEqual(AmfConfig.Configuration.Security, amfConfig.Configuration.Security) { 92 | logger.CfgLog.Infoln("updated Security:", amfConfig.Configuration.Security) 93 | } 94 | if !reflect.DeepEqual(AmfConfig.Configuration.NetworkName, amfConfig.Configuration.NetworkName) { 95 | logger.CfgLog.Infoln("updated NetworkName:", amfConfig.Configuration.NetworkName) 96 | } 97 | if !reflect.DeepEqual(AmfConfig.Configuration.T3502Value, amfConfig.Configuration.T3502Value) { 98 | logger.CfgLog.Infoln("updated T3502Value:", amfConfig.Configuration.T3502Value) 99 | } 100 | if !reflect.DeepEqual(AmfConfig.Configuration.T3512Value, amfConfig.Configuration.T3512Value) { 101 | logger.CfgLog.Infoln("updated T3512Value:", amfConfig.Configuration.T3512Value) 102 | } 103 | if !reflect.DeepEqual(AmfConfig.Configuration.Non3gppDeregistrationTimerValue, amfConfig.Configuration.Non3gppDeregistrationTimerValue) { 104 | logger.CfgLog.Infoln("updated Non3gppDeregistrationTimerValue:", amfConfig.Configuration.Non3gppDeregistrationTimerValue) 105 | } 106 | if !reflect.DeepEqual(AmfConfig.Configuration.T3513, amfConfig.Configuration.T3513) { 107 | logger.CfgLog.Infoln("updated T3513:", amfConfig.Configuration.T3513) 108 | } 109 | if !reflect.DeepEqual(AmfConfig.Configuration.T3522, amfConfig.Configuration.T3522) { 110 | logger.CfgLog.Infoln("updated T3522:", amfConfig.Configuration.T3522) 111 | } 112 | if !reflect.DeepEqual(AmfConfig.Configuration.T3550, amfConfig.Configuration.T3550) { 113 | logger.CfgLog.Infoln("updated T3550:", amfConfig.Configuration.T3550) 114 | } 115 | if !reflect.DeepEqual(AmfConfig.Configuration.T3560, amfConfig.Configuration.T3560) { 116 | logger.CfgLog.Infoln("updated T3560:", amfConfig.Configuration.T3560) 117 | } 118 | if !reflect.DeepEqual(AmfConfig.Configuration.T3565, amfConfig.Configuration.T3565) { 119 | logger.CfgLog.Infoln("updated T3565:", amfConfig.Configuration.T3565) 120 | } 121 | 122 | amfConfig.Rcvd = true 123 | AmfConfig = amfConfig 124 | } 125 | return nil 126 | } 127 | 128 | func CheckConfigVersion() error { 129 | currentVersion := AmfConfig.GetVersion() 130 | 131 | if currentVersion != AMF_EXPECTED_CONFIG_VERSION { 132 | return fmt.Errorf("config version is [%s], but expected is [%s]", 133 | currentVersion, AMF_EXPECTED_CONFIG_VERSION) 134 | } 135 | 136 | logger.CfgLog.Infof("config version [%s]", currentVersion) 137 | 138 | return nil 139 | } 140 | -------------------------------------------------------------------------------- /gmm/init.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package gmm 8 | 9 | import ( 10 | "github.com/omec-project/amf/context" 11 | "github.com/omec-project/amf/logger" 12 | "github.com/omec-project/util/fsm" 13 | ) 14 | 15 | const ( 16 | GmmMessageEvent fsm.EventType = "Gmm Message" 17 | StartAuthEvent fsm.EventType = "Start Authentication" 18 | AuthSuccessEvent fsm.EventType = "Authentication Success" 19 | AuthRestartEvent fsm.EventType = "Authentication Restart" 20 | AuthFailEvent fsm.EventType = "Authentication Fail" 21 | AuthErrorEvent fsm.EventType = "Authentication Error" 22 | SecurityModeSuccessEvent fsm.EventType = "SecurityMode Success" 23 | SecurityModeFailEvent fsm.EventType = "SecurityMode Fail" 24 | SecuritySkipEvent fsm.EventType = "Security Skip" 25 | SecurityModeAbortEvent fsm.EventType = "SecurityMode Abort" 26 | ContextSetupSuccessEvent fsm.EventType = "ContextSetup Success" 27 | ContextSetupFailEvent fsm.EventType = "ContextSetup Fail" 28 | InitDeregistrationEvent fsm.EventType = "Initialize Deregistration" 29 | NwInitiatedDeregistrationEvent fsm.EventType = "Network Initiated Deregistration Event" 30 | SliceInfoDeleteEvent fsm.EventType = "Slice Info Delete Event" 31 | SliceInfoAddEvent fsm.EventType = "Slice Info Add Event" 32 | DeregistrationAcceptEvent fsm.EventType = "Deregistration Accept" 33 | ) 34 | 35 | const ( 36 | ArgAmfUe string = "AMF Ue" 37 | ArgNASMessage string = "NAS Message" 38 | ArgProcedureCode string = "Procedure Code" 39 | ArgAccessType string = "Access Type" 40 | ArgEAPSuccess string = "EAP Success" 41 | ArgEAPMessage string = "EAP Message" 42 | Arg3GPPDeregistered string = "3GPP Deregistered" 43 | ArgNon3GPPDeregistered string = "Non3GPP Deregistered" 44 | ArgNssai string = "Nssai" 45 | ) 46 | 47 | var transitions = fsm.Transitions{ 48 | {Event: GmmMessageEvent, From: context.Deregistered, To: context.Deregistered}, 49 | {Event: GmmMessageEvent, From: context.Authentication, To: context.Authentication}, 50 | {Event: GmmMessageEvent, From: context.SecurityMode, To: context.SecurityMode}, 51 | {Event: GmmMessageEvent, From: context.ContextSetup, To: context.ContextSetup}, 52 | {Event: GmmMessageEvent, From: context.Registered, To: context.Registered}, 53 | {Event: GmmMessageEvent, From: context.DeregistrationInitiated, To: context.DeregistrationInitiated}, 54 | {Event: StartAuthEvent, From: context.Deregistered, To: context.Authentication}, 55 | {Event: StartAuthEvent, From: context.Registered, To: context.Authentication}, 56 | {Event: AuthRestartEvent, From: context.Authentication, To: context.Authentication}, 57 | {Event: AuthSuccessEvent, From: context.Authentication, To: context.SecurityMode}, 58 | {Event: AuthFailEvent, From: context.Authentication, To: context.Deregistered}, 59 | {Event: AuthErrorEvent, From: context.Authentication, To: context.Deregistered}, 60 | {Event: SecurityModeSuccessEvent, From: context.SecurityMode, To: context.ContextSetup}, 61 | {Event: SecuritySkipEvent, From: context.SecurityMode, To: context.ContextSetup}, 62 | {Event: SecurityModeFailEvent, From: context.SecurityMode, To: context.Deregistered}, 63 | {Event: SecurityModeAbortEvent, From: context.SecurityMode, To: context.Deregistered}, 64 | {Event: ContextSetupSuccessEvent, From: context.ContextSetup, To: context.Registered}, 65 | {Event: ContextSetupFailEvent, From: context.ContextSetup, To: context.Deregistered}, 66 | {Event: InitDeregistrationEvent, From: context.Registered, To: context.DeregistrationInitiated}, 67 | {Event: NwInitiatedDeregistrationEvent, From: context.Registered, To: context.DeregistrationInitiated}, 68 | {Event: DeregistrationAcceptEvent, From: context.DeregistrationInitiated, To: context.Deregistered}, 69 | } 70 | 71 | var callbacks = fsm.Callbacks{ 72 | context.Deregistered: DeRegistered, 73 | context.Authentication: Authentication, 74 | context.SecurityMode: SecurityMode, 75 | context.ContextSetup: ContextSetup, 76 | context.Registered: Registered, 77 | context.DeregistrationInitiated: DeregisteredInitiated, 78 | } 79 | 80 | var GmmFSM *fsm.FSM 81 | 82 | func init() { 83 | if f, err := fsm.NewFSM(transitions, callbacks); err != nil { 84 | logger.GmmLog.Errorf("Initialize Gmm FSM Error: %+v", err) 85 | } else { 86 | GmmFSM = f 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /gmm/init_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package gmm_test 8 | 9 | import ( 10 | "fmt" 11 | "testing" 12 | 13 | "github.com/omec-project/amf/gmm" 14 | "github.com/omec-project/util/fsm" 15 | ) 16 | 17 | func TestGmmFSM(t *testing.T) { 18 | if err := fsm.ExportDot(gmm.GmmFSM, "gmm"); err != nil { 19 | fmt.Printf("fsm export data return error: %+v", err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /gmm/mock_gmm.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Open Networking Foundation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package gmm 7 | 8 | import ( 9 | "github.com/omec-project/amf/context" 10 | "github.com/omec-project/amf/logger" 11 | "github.com/omec-project/util/fsm" 12 | ) 13 | 14 | var ( 15 | MockRegisteredCallCount uint32 = 0 16 | MockDeregisteredInitiatedCallCount uint32 = 0 17 | MockContextSetupCallCount uint32 = 0 18 | MockDeRegisteredCallCount uint32 = 0 19 | MockSecurityModeCallCount uint32 = 0 20 | MockAuthenticationCallCount uint32 = 0 21 | ) 22 | 23 | var mockCallbacks = fsm.Callbacks{ 24 | context.Deregistered: MockDeRegistered, 25 | context.Authentication: MockAuthentication, 26 | context.SecurityMode: MockSecurityMode, 27 | context.ContextSetup: MockContextSetup, 28 | context.Registered: MockRegistered, 29 | context.DeregistrationInitiated: MockDeregisteredInitiated, 30 | } 31 | 32 | func Mockinit() { 33 | if f, err := fsm.NewFSM(transitions, mockCallbacks); err != nil { 34 | logger.GmmLog.Errorf("Initialize Gmm FSM Error: %+v", err) 35 | } else { 36 | GmmFSM = f 37 | } 38 | } 39 | 40 | func MockDeRegistered(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { 41 | logger.GmmLog.Info("MockDeRegistered") 42 | MockDeRegisteredCallCount++ 43 | } 44 | 45 | func MockAuthentication(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { 46 | logger.GmmLog.Info("MockAuthentication") 47 | MockAuthenticationCallCount++ 48 | } 49 | 50 | func MockSecurityMode(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { 51 | logger.GmmLog.Info("MockSecurityMode") 52 | MockSecurityModeCallCount++ 53 | } 54 | 55 | func MockContextSetup(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { 56 | logger.GmmLog.Info("MockContextSetup") 57 | MockContextSetupCallCount++ 58 | } 59 | 60 | func MockRegistered(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { 61 | logger.GmmLog.Info(event) 62 | logger.GmmLog.Info("MockRegistered") 63 | MockRegisteredCallCount++ 64 | } 65 | 66 | func MockDeregisteredInitiated(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { 67 | logger.GmmLog.Info("MockDeregisteredInitiated") 68 | MockDeregisteredInitiatedCallCount++ 69 | 70 | amfUe := args[ArgAmfUe].(*context.AmfUe) 71 | amfUe.Remove() 72 | } 73 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/omec-project/amf 2 | 3 | go 1.24.0 4 | 5 | require ( 6 | git.cs.nctu.edu.tw/calee/sctp v1.1.0 7 | github.com/antihax/optional v1.0.0 8 | github.com/fsnotify/fsnotify v1.9.0 9 | github.com/gin-contrib/cors v1.7.5 10 | github.com/gin-gonic/gin v1.10.1 11 | github.com/google/uuid v1.6.0 12 | github.com/mitchellh/mapstructure v1.5.0 13 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 14 | github.com/omec-project/aper v1.3.0 15 | github.com/omec-project/config5g v1.6.0 16 | github.com/omec-project/nas v1.5.2 17 | github.com/omec-project/ngap v1.4.0 18 | github.com/omec-project/openapi v1.4.1 19 | github.com/omec-project/util v1.3.2 20 | github.com/prometheus/client_golang v1.22.0 21 | github.com/spf13/viper v1.20.1 22 | github.com/stretchr/testify v1.10.0 23 | github.com/urfave/cli v1.22.16 24 | go.mongodb.org/mongo-driver v1.17.3 25 | google.golang.org/grpc v1.72.2 26 | google.golang.org/protobuf v1.36.6 27 | gopkg.in/yaml.v2 v2.4.0 28 | ) 29 | 30 | require ( 31 | github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 // indirect 32 | github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect 33 | github.com/avast/retry-go/v4 v4.1.0 // indirect 34 | github.com/beorn7/perks v1.0.1 // indirect 35 | github.com/bytedance/sonic v1.13.2 // indirect 36 | github.com/bytedance/sonic/loader v0.2.4 // indirect 37 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 38 | github.com/cloudwego/base64x v0.1.5 // indirect 39 | github.com/coreos/go-semver v0.3.0 // indirect 40 | github.com/coreos/go-systemd/v22 v22.3.2 // indirect 41 | github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect 42 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 43 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 44 | github.com/evanphx/json-patch v5.9.11+incompatible // indirect 45 | github.com/gabriel-vasile/mimetype v1.4.8 // indirect 46 | github.com/gin-contrib/sse v1.0.0 // indirect 47 | github.com/go-playground/locales v0.14.1 // indirect 48 | github.com/go-playground/universal-translator v0.18.1 // indirect 49 | github.com/go-playground/validator/v10 v10.26.0 // indirect 50 | github.com/go-redis/redis/v8 v8.11.5 // indirect 51 | github.com/go-viper/mapstructure/v2 v2.2.1 // indirect 52 | github.com/goccy/go-json v0.10.5 // indirect 53 | github.com/gogo/protobuf v1.3.2 // indirect 54 | github.com/golang-jwt/jwt/v5 v5.2.2 // indirect 55 | github.com/golang/protobuf v1.5.4 // indirect 56 | github.com/golang/snappy v0.0.4 // indirect 57 | github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect 58 | github.com/jmoiron/sqlx v1.3.5 // indirect 59 | github.com/json-iterator/go v1.1.12 // indirect 60 | github.com/klauspost/compress v1.18.0 // indirect 61 | github.com/klauspost/cpuid/v2 v2.2.10 // indirect 62 | github.com/leodido/go-urn v1.4.0 // indirect 63 | github.com/lib/pq v1.10.6 // indirect 64 | github.com/magiconair/properties v1.8.7 // indirect 65 | github.com/mattn/go-isatty v0.0.20 // indirect 66 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 67 | github.com/modern-go/reflect2 v1.0.2 // indirect 68 | github.com/montanaflynn/stats v0.7.1 // indirect 69 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 70 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 71 | github.com/pierrec/lz4/v4 v4.1.15 // indirect 72 | github.com/pkg/errors v0.9.1 // indirect 73 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 74 | github.com/prometheus/client_model v0.6.1 // indirect 75 | github.com/prometheus/common v0.62.0 // indirect 76 | github.com/prometheus/procfs v0.15.1 // indirect 77 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 78 | github.com/sagikazarmark/locafero v0.7.0 // indirect 79 | github.com/segmentio/kafka-go v0.4.48 80 | github.com/sourcegraph/conc v0.3.0 // indirect 81 | github.com/spf13/afero v1.12.0 // indirect 82 | github.com/spf13/cast v1.7.1 // indirect 83 | github.com/spf13/pflag v1.0.6 // indirect 84 | github.com/subosito/gotenv v1.6.0 // indirect 85 | github.com/thakurajayL/go-ipam v0.0.5-dev // indirect 86 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 87 | github.com/ugorji/go/codec v1.2.12 // indirect 88 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 89 | github.com/xdg-go/scram v1.1.2 // indirect 90 | github.com/xdg-go/stringprep v1.0.4 // indirect 91 | github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect 92 | go.etcd.io/etcd/api/v3 v3.5.12 // indirect 93 | go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect 94 | go.etcd.io/etcd/client/v3 v3.5.12 // indirect 95 | go.uber.org/multierr v1.11.0 // indirect 96 | go.uber.org/zap v1.27.0 97 | go4.org/intern v0.0.0-20220617035311-6925f38cc365 // indirect 98 | go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect 99 | golang.org/x/arch v0.15.0 // indirect 100 | golang.org/x/crypto v0.38.0 // indirect 101 | golang.org/x/net v0.40.0 // indirect 102 | golang.org/x/oauth2 v0.30.0 // indirect 103 | golang.org/x/sync v0.14.0 // indirect 104 | golang.org/x/sys v0.33.0 // indirect 105 | golang.org/x/text v0.25.0 // indirect 106 | google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect 107 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect 108 | gopkg.in/h2non/gock.v1 v1.1.2 // indirect 109 | gopkg.in/yaml.v3 v3.0.1 // indirect 110 | inet.af/netaddr v0.0.0-20220811202034-502d2d690317 // indirect 111 | ) 112 | -------------------------------------------------------------------------------- /go.mod.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | Copyright 2019 free5GC.org 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | -------------------------------------------------------------------------------- /go.sum.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | Copyright 2019 free5GC.org 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | -------------------------------------------------------------------------------- /httpcallback/api_am_policy_control_update_notify.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package httpcallback 7 | 8 | import ( 9 | "net/http" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/omec-project/amf/logger" 13 | "github.com/omec-project/amf/producer" 14 | "github.com/omec-project/openapi" 15 | "github.com/omec-project/openapi/models" 16 | "github.com/omec-project/util/httpwrapper" 17 | ) 18 | 19 | func HTTPAmPolicyControlUpdateNotifyUpdate(c *gin.Context) { 20 | var policyUpdate models.PolicyUpdate 21 | 22 | requestBody, err := c.GetRawData() 23 | if err != nil { 24 | logger.CallbackLog.Errorf("Get Request Body error: %+v", err) 25 | problemDetail := models.ProblemDetails{ 26 | Title: "System failure", 27 | Status: http.StatusInternalServerError, 28 | Detail: err.Error(), 29 | Cause: "SYSTEM_FAILURE", 30 | } 31 | c.JSON(http.StatusInternalServerError, problemDetail) 32 | return 33 | } 34 | 35 | err = openapi.Deserialize(&policyUpdate, requestBody, "application/json") 36 | if err != nil { 37 | problemDetail := "[Request Body] " + err.Error() 38 | rsp := models.ProblemDetails{ 39 | Title: "Malformed request syntax", 40 | Status: http.StatusBadRequest, 41 | Detail: problemDetail, 42 | } 43 | logger.CallbackLog.Errorln(problemDetail) 44 | c.JSON(http.StatusBadRequest, rsp) 45 | return 46 | } 47 | 48 | req := httpwrapper.NewRequest(c.Request, policyUpdate) 49 | req.Params["polAssoId"] = c.Params.ByName("polAssoId") 50 | 51 | rsp := producer.HandleAmPolicyControlUpdateNotifyUpdate(req) 52 | 53 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 54 | if err != nil { 55 | logger.CallbackLog.Errorln(err) 56 | problemDetails := models.ProblemDetails{ 57 | Status: http.StatusInternalServerError, 58 | Cause: "SYSTEM_FAILURE", 59 | Detail: err.Error(), 60 | } 61 | c.JSON(http.StatusInternalServerError, problemDetails) 62 | } else { 63 | c.Data(rsp.Status, "application/json", responseBody) 64 | } 65 | } 66 | 67 | func HTTPAmPolicyControlUpdateNotifyTerminate(c *gin.Context) { 68 | var terminationNotification models.TerminationNotification 69 | 70 | requestBody, err := c.GetRawData() 71 | if err != nil { 72 | logger.CallbackLog.Errorf("Get Request Body error: %+v", err) 73 | problemDetail := models.ProblemDetails{ 74 | Title: "System failure", 75 | Status: http.StatusInternalServerError, 76 | Detail: err.Error(), 77 | Cause: "SYSTEM_FAILURE", 78 | } 79 | c.JSON(http.StatusInternalServerError, problemDetail) 80 | return 81 | } 82 | 83 | err = openapi.Deserialize(&terminationNotification, requestBody, "application/json") 84 | if err != nil { 85 | problemDetail := "[Request Body] " + err.Error() 86 | rsp := models.ProblemDetails{ 87 | Title: "Malformed request syntax", 88 | Status: http.StatusBadRequest, 89 | Detail: problemDetail, 90 | } 91 | logger.CallbackLog.Errorln(problemDetail) 92 | c.JSON(http.StatusBadRequest, rsp) 93 | return 94 | } 95 | 96 | req := httpwrapper.NewRequest(c.Request, terminationNotification) 97 | req.Params["polAssoId"] = c.Params.ByName("polAssoId") 98 | 99 | rsp := producer.HandleAmPolicyControlUpdateNotifyTerminate(req) 100 | 101 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 102 | if err != nil { 103 | logger.CallbackLog.Errorln(err) 104 | problemDetails := models.ProblemDetails{ 105 | Status: http.StatusInternalServerError, 106 | Cause: "SYSTEM_FAILURE", 107 | Detail: err.Error(), 108 | } 109 | c.JSON(http.StatusInternalServerError, problemDetails) 110 | } else { 111 | c.Data(rsp.Status, "application/json", responseBody) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /httpcallback/api_dereg_notify.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Infosys Limited 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package httpcallback 8 | 9 | import ( 10 | "net/http" 11 | 12 | "github.com/gin-gonic/gin" 13 | "github.com/omec-project/amf/logger" 14 | "github.com/omec-project/amf/producer" 15 | "github.com/omec-project/openapi" 16 | "github.com/omec-project/openapi/models" 17 | "github.com/omec-project/util/httpwrapper" 18 | ) 19 | 20 | func HTTPDeregistrationNotification(c *gin.Context) { 21 | var deregistrationData models.DeregistrationData 22 | 23 | requestBody, err := c.GetRawData() 24 | if err != nil { 25 | logger.CallbackLog.Errorf("Get Request Body error: %+v", err) 26 | problemDetail := models.ProblemDetails{ 27 | Title: "System failure", 28 | Status: http.StatusInternalServerError, 29 | Detail: err.Error(), 30 | Cause: "SYSTEM_FAILURE", 31 | } 32 | c.JSON(http.StatusInternalServerError, problemDetail) 33 | return 34 | } 35 | 36 | err = openapi.Deserialize(&deregistrationData, requestBody, "application/json") 37 | if err != nil { 38 | problemDetail := "[Request Body] " + err.Error() 39 | rsp := models.ProblemDetails{ 40 | Title: "Malformed request syntax", 41 | Status: http.StatusBadRequest, 42 | Detail: problemDetail, 43 | } 44 | logger.CallbackLog.Errorln(problemDetail) 45 | c.JSON(http.StatusBadRequest, rsp) 46 | return 47 | } 48 | 49 | req := httpwrapper.NewRequest(c.Request, deregistrationData) 50 | if supi, exists := c.Params.Get("supi"); exists { 51 | req.Params["supi"] = supi 52 | } 53 | rsp := producer.HandleDeregistrationNotification(req) 54 | 55 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 56 | if err != nil { 57 | logger.CallbackLog.Errorln(err) 58 | problemDetails := models.ProblemDetails{ 59 | Status: http.StatusInternalServerError, 60 | Cause: "SYSTEM_FAILURE", 61 | Detail: err.Error(), 62 | } 63 | c.JSON(http.StatusInternalServerError, problemDetails) 64 | } else { 65 | c.Data(rsp.Status, "application/json", responseBody) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /httpcallback/api_n1_message_notify.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package httpcallback 7 | 8 | import ( 9 | "net/http" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/omec-project/amf/logger" 13 | "github.com/omec-project/amf/producer" 14 | "github.com/omec-project/openapi" 15 | "github.com/omec-project/openapi/models" 16 | "github.com/omec-project/util/httpwrapper" 17 | ) 18 | 19 | func HTTPN1MessageNotify(c *gin.Context) { 20 | var n1MessageNotification models.N1MessageNotification 21 | 22 | requestBody, err := c.GetRawData() 23 | if err != nil { 24 | logger.CallbackLog.Errorf("Get Request Body error: %+v", err) 25 | problemDetail := models.ProblemDetails{ 26 | Title: "System failure", 27 | Status: http.StatusInternalServerError, 28 | Detail: err.Error(), 29 | Cause: "SYSTEM_FAILURE", 30 | } 31 | c.JSON(http.StatusInternalServerError, problemDetail) 32 | return 33 | } 34 | 35 | err = openapi.Deserialize(&n1MessageNotification, requestBody, "application/json") 36 | if err != nil { 37 | problemDetail := "[Request Body] " + err.Error() 38 | rsp := models.ProblemDetails{ 39 | Title: "Malformed request syntax", 40 | Status: http.StatusBadRequest, 41 | Detail: problemDetail, 42 | } 43 | logger.CallbackLog.Errorln(problemDetail) 44 | c.JSON(http.StatusBadRequest, rsp) 45 | return 46 | } 47 | 48 | req := httpwrapper.NewRequest(c.Request, n1MessageNotification) 49 | 50 | rsp := producer.HandleN1MessageNotify(req) 51 | 52 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 53 | if err != nil { 54 | logger.CallbackLog.Errorln(err) 55 | problemDetails := models.ProblemDetails{ 56 | Status: http.StatusInternalServerError, 57 | Cause: "SYSTEM_FAILURE", 58 | Detail: err.Error(), 59 | } 60 | c.JSON(http.StatusInternalServerError, problemDetails) 61 | } else { 62 | c.Data(rsp.Status, "application/json", responseBody) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /httpcallback/api_nf_subscribe_notify.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Infosys Limited 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package httpcallback 8 | 9 | import ( 10 | "net/http" 11 | 12 | "github.com/gin-gonic/gin" 13 | "github.com/omec-project/amf/logger" 14 | "github.com/omec-project/amf/producer" 15 | "github.com/omec-project/openapi" 16 | "github.com/omec-project/openapi/models" 17 | "github.com/omec-project/util/httpwrapper" 18 | ) 19 | 20 | func HTTPNfSubscriptionStatusNotify(c *gin.Context) { 21 | var nfSubscriptionStatusNotification models.NotificationData 22 | 23 | requestBody, err := c.GetRawData() 24 | if err != nil { 25 | logger.CallbackLog.Errorf("Get Request Body error: %+v", err) 26 | problemDetail := models.ProblemDetails{ 27 | Title: "System failure", 28 | Status: http.StatusInternalServerError, 29 | Detail: err.Error(), 30 | Cause: "SYSTEM_FAILURE", 31 | } 32 | c.JSON(http.StatusInternalServerError, problemDetail) 33 | return 34 | } 35 | 36 | err = openapi.Deserialize(&nfSubscriptionStatusNotification, requestBody, "application/json") 37 | if err != nil { 38 | problemDetail := "[Request Body] " + err.Error() 39 | rsp := models.ProblemDetails{ 40 | Title: "Malformed request syntax", 41 | Status: http.StatusBadRequest, 42 | Detail: problemDetail, 43 | } 44 | logger.CallbackLog.Errorln(problemDetail) 45 | c.JSON(http.StatusBadRequest, rsp) 46 | return 47 | } 48 | 49 | req := httpwrapper.NewRequest(c.Request, nfSubscriptionStatusNotification) 50 | 51 | rsp := producer.HandleNfSubscriptionStatusNotify(req) 52 | 53 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 54 | if err != nil { 55 | logger.CallbackLog.Errorln(err) 56 | problemDetails := models.ProblemDetails{ 57 | Status: http.StatusInternalServerError, 58 | Cause: "SYSTEM_FAILURE", 59 | Detail: err.Error(), 60 | } 61 | c.JSON(http.StatusInternalServerError, problemDetails) 62 | } else if rsp.Body != nil { 63 | c.Data(rsp.Status, "application/json", responseBody) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /httpcallback/api_sm_context_status_notify.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package httpcallback 7 | 8 | import ( 9 | "net/http" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/omec-project/amf/logger" 13 | "github.com/omec-project/amf/producer" 14 | "github.com/omec-project/openapi" 15 | "github.com/omec-project/openapi/models" 16 | "github.com/omec-project/util/httpwrapper" 17 | ) 18 | 19 | func HTTPSmContextStatusNotify(c *gin.Context) { 20 | var smContextStatusNotification models.SmContextStatusNotification 21 | 22 | requestBody, err := c.GetRawData() 23 | if err != nil { 24 | logger.CallbackLog.Errorf("Get Request Body error: %+v", err) 25 | problemDetail := models.ProblemDetails{ 26 | Title: "System failure", 27 | Status: http.StatusInternalServerError, 28 | Detail: err.Error(), 29 | Cause: "SYSTEM_FAILURE", 30 | } 31 | c.JSON(http.StatusInternalServerError, problemDetail) 32 | return 33 | } 34 | 35 | err = openapi.Deserialize(&smContextStatusNotification, requestBody, "application/json") 36 | if err != nil { 37 | problemDetail := "[Request Body] " + err.Error() 38 | rsp := models.ProblemDetails{ 39 | Title: "Malformed request syntax", 40 | Status: http.StatusBadRequest, 41 | Detail: problemDetail, 42 | } 43 | logger.CallbackLog.Errorln(problemDetail) 44 | c.JSON(http.StatusBadRequest, rsp) 45 | return 46 | } 47 | 48 | req := httpwrapper.NewRequest(c.Request, smContextStatusNotification) 49 | req.Params["guti"] = c.Params.ByName("guti") 50 | req.Params["pduSessionId"] = c.Params.ByName("pduSessionId") 51 | 52 | rsp := producer.HandleSmContextStatusNotify(req) 53 | 54 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 55 | if err != nil { 56 | logger.CallbackLog.Errorln(err) 57 | problemDetails := models.ProblemDetails{ 58 | Status: http.StatusInternalServerError, 59 | Cause: "SYSTEM_FAILURE", 60 | Detail: err.Error(), 61 | } 62 | c.JSON(http.StatusInternalServerError, problemDetails) 63 | } else { 64 | c.Data(rsp.Status, "application/json", responseBody) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /httpcallback/router.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Intel Corporation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package httpcallback 8 | 9 | import ( 10 | "net/http" 11 | "strings" 12 | 13 | "github.com/gin-gonic/gin" 14 | "github.com/omec-project/amf/logger" 15 | utilLogger "github.com/omec-project/util/logger" 16 | ) 17 | 18 | // Route is the information for every URI. 19 | type Route struct { 20 | // Name is the name of this Route. 21 | Name string 22 | // Method is the string for the HTTP method. ex) GET, POST etc.. 23 | Method string 24 | // Pattern is the pattern of the URI. 25 | Pattern string 26 | // HandlerFunc is the handler function of this route. 27 | HandlerFunc gin.HandlerFunc 28 | } 29 | 30 | // Routes is the list of the generated Route. 31 | type Routes []Route 32 | 33 | // NewRouter returns a new router. 34 | func NewRouter() *gin.Engine { 35 | router := utilLogger.NewGinWithZap(logger.GinLog) 36 | AddService(router) 37 | return router 38 | } 39 | 40 | func AddService(engine *gin.Engine) *gin.RouterGroup { 41 | group := engine.Group("/namf-callback/v1") 42 | 43 | for _, route := range routes { 44 | switch route.Method { 45 | case "GET": 46 | group.GET(route.Pattern, route.HandlerFunc) 47 | case "POST": 48 | group.POST(route.Pattern, route.HandlerFunc) 49 | case "PUT": 50 | group.PUT(route.Pattern, route.HandlerFunc) 51 | case "PATCH": 52 | group.PATCH(route.Pattern, route.HandlerFunc) 53 | case "DELETE": 54 | group.DELETE(route.Pattern, route.HandlerFunc) 55 | } 56 | } 57 | return group 58 | } 59 | 60 | // Index is the index handler. 61 | func Index(c *gin.Context) { 62 | c.String(http.StatusOK, "Hello World!") 63 | } 64 | 65 | var routes = Routes{ 66 | { 67 | "Index", 68 | "GET", 69 | "/", 70 | Index, 71 | }, 72 | 73 | { 74 | "SmContextStatusNotify", 75 | strings.ToUpper("Post"), 76 | "/smContextStatus/:guti/:pduSessionId", 77 | HTTPSmContextStatusNotify, 78 | }, 79 | 80 | { 81 | "AmPolicyControlUpdateNotifyUpdate", 82 | strings.ToUpper("Post"), 83 | "/am-policy/:polAssoId/update", 84 | HTTPAmPolicyControlUpdateNotifyUpdate, 85 | }, 86 | 87 | { 88 | "AmPolicyControlUpdateNotifyTerminate", 89 | strings.ToUpper("Post"), 90 | "/am-policy/:polAssoId/terminate", 91 | HTTPAmPolicyControlUpdateNotifyTerminate, 92 | }, 93 | 94 | { 95 | "N1MessageNotify", 96 | strings.ToUpper("Post"), 97 | "/n1-message-notify", 98 | HTTPN1MessageNotify, 99 | }, 100 | { 101 | "NfStatusNotify", 102 | strings.ToUpper("Post"), 103 | "/nf-status-notify", 104 | HTTPNfSubscriptionStatusNotify, 105 | }, 106 | { 107 | "DeregistrationNotify", 108 | strings.ToUpper("Post"), 109 | ":supi/deregistration-notify", 110 | HTTPDeregistrationNotification, 111 | }, 112 | } 113 | -------------------------------------------------------------------------------- /location/api_individual_ue_context_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Location 8 | * 9 | * AMF Location Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package location 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | "github.com/omec-project/amf/producer" 23 | "github.com/omec-project/openapi" 24 | "github.com/omec-project/openapi/models" 25 | "github.com/omec-project/util/httpwrapper" 26 | ) 27 | 28 | // ProvideLocationInfo - Namf_Location ProvideLocationInfo service Operation 29 | func HTTPProvideLocationInfo(c *gin.Context) { 30 | var requestLocInfo models.RequestLocInfo 31 | 32 | requestBody, err := c.GetRawData() 33 | if err != nil { 34 | problemDetail := models.ProblemDetails{ 35 | Title: "System failure", 36 | Status: http.StatusInternalServerError, 37 | Detail: err.Error(), 38 | Cause: "SYSTEM_FAILURE", 39 | } 40 | logger.LocationLog.Errorf("Get Request Body error: %+v", err) 41 | c.JSON(http.StatusInternalServerError, problemDetail) 42 | return 43 | } 44 | 45 | err = openapi.Deserialize(&requestLocInfo, requestBody, "application/json") 46 | if err != nil { 47 | problemDetail := "[Request Body] " + err.Error() 48 | rsp := models.ProblemDetails{ 49 | Title: "Malformed request syntax", 50 | Status: http.StatusBadRequest, 51 | Detail: problemDetail, 52 | } 53 | logger.LocationLog.Errorln(problemDetail) 54 | c.JSON(http.StatusBadRequest, rsp) 55 | return 56 | } 57 | 58 | req := httpwrapper.NewRequest(c.Request, requestLocInfo) 59 | req.Params["ueContextId"] = c.Params.ByName("ueContextId") 60 | 61 | rsp := producer.HandleProvideLocationInfoRequest(req) 62 | 63 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 64 | if err != nil { 65 | logger.CommLog.Errorln(err) 66 | problemDetails := models.ProblemDetails{ 67 | Status: http.StatusInternalServerError, 68 | Cause: "SYSTEM_FAILURE", 69 | Detail: err.Error(), 70 | } 71 | c.JSON(http.StatusInternalServerError, problemDetails) 72 | } else { 73 | c.Data(rsp.Status, "application/json", responseBody) 74 | } 75 | } 76 | 77 | // ProvidePositioningInfo - Namf_Location ProvidePositioningInfo service Operation 78 | func HTTPProvidePositioningInfo(c *gin.Context) { 79 | logger.LocationLog.Warnf("Handle Provide Positioning Info is not implemented.") 80 | c.JSON(http.StatusOK, gin.H{}) 81 | } 82 | -------------------------------------------------------------------------------- /location/routers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_Location 8 | * 9 | * AMF Location Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package location 16 | 17 | import ( 18 | "net/http" 19 | "strings" 20 | 21 | "github.com/gin-gonic/gin" 22 | "github.com/omec-project/amf/logger" 23 | utilLogger "github.com/omec-project/util/logger" 24 | ) 25 | 26 | // Route is the information for every URI. 27 | type Route struct { 28 | // Name is the name of this Route. 29 | Name string 30 | // Method is the string for the HTTP method. ex) GET, POST etc.. 31 | Method string 32 | // Pattern is the pattern of the URI. 33 | Pattern string 34 | // HandlerFunc is the handler function of this route. 35 | HandlerFunc gin.HandlerFunc 36 | } 37 | 38 | // Routes is the list of the generated Route. 39 | type Routes []Route 40 | 41 | // NewRouter returns a new router. 42 | func NewRouter() *gin.Engine { 43 | router := utilLogger.NewGinWithZap(logger.GinLog) 44 | AddService(router) 45 | return router 46 | } 47 | 48 | func AddService(engine *gin.Engine) *gin.RouterGroup { 49 | group := engine.Group("/namf-loc/v1") 50 | 51 | for _, route := range routes { 52 | switch route.Method { 53 | case "GET": 54 | group.GET(route.Pattern, route.HandlerFunc) 55 | case "POST": 56 | group.POST(route.Pattern, route.HandlerFunc) 57 | case "PUT": 58 | group.PUT(route.Pattern, route.HandlerFunc) 59 | case "DELETE": 60 | group.DELETE(route.Pattern, route.HandlerFunc) 61 | case "PATCH": 62 | group.PATCH(route.Pattern, route.HandlerFunc) 63 | } 64 | } 65 | return group 66 | } 67 | 68 | // Index is the index handler. 69 | func Index(c *gin.Context) { 70 | c.String(http.StatusOK, "Hello World!") 71 | } 72 | 73 | var routes = Routes{ 74 | { 75 | "Index", 76 | "GET", 77 | "/", 78 | Index, 79 | }, 80 | 81 | { 82 | "ProvideLocationInfo", 83 | strings.ToUpper("Post"), 84 | "/:ueContextId/provide-loc-info", 85 | HTTPProvideLocationInfo, 86 | }, 87 | 88 | { 89 | "ProvidePositioningInfo", 90 | strings.ToUpper("Post"), 91 | "/:ueContextId/provide-pos-info", 92 | HTTPProvidePositioningInfo, 93 | }, 94 | } 95 | -------------------------------------------------------------------------------- /logger/logger.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Intel Corporation 2 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 3 | // Copyright 2019 free5GC.org 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | 7 | package logger 8 | 9 | import ( 10 | "go.uber.org/zap" 11 | "go.uber.org/zap/zapcore" 12 | ) 13 | 14 | var ( 15 | log *zap.Logger 16 | AppLog *zap.SugaredLogger 17 | InitLog *zap.SugaredLogger 18 | CfgLog *zap.SugaredLogger 19 | ContextLog *zap.SugaredLogger 20 | DataRepoLog *zap.SugaredLogger 21 | NgapLog *zap.SugaredLogger 22 | HandlerLog *zap.SugaredLogger 23 | HttpLog *zap.SugaredLogger 24 | GmmLog *zap.SugaredLogger 25 | MtLog *zap.SugaredLogger 26 | ProducerLog *zap.SugaredLogger 27 | LocationLog *zap.SugaredLogger 28 | CommLog *zap.SugaredLogger 29 | CallbackLog *zap.SugaredLogger 30 | UtilLog *zap.SugaredLogger 31 | NasLog *zap.SugaredLogger 32 | ConsumerLog *zap.SugaredLogger 33 | EeLog *zap.SugaredLogger 34 | GinLog *zap.SugaredLogger 35 | GrpcLog *zap.SugaredLogger 36 | KafkaLog *zap.SugaredLogger 37 | atomicLevel zap.AtomicLevel 38 | ) 39 | 40 | const ( 41 | FieldRanAddr string = "ran_addr" 42 | FieldRanId string = "ran_id" 43 | FieldAmfUeNgapID string = "amf_ue_ngap_id" 44 | FieldSupi string = "supi" 45 | FieldSuci string = "suci" 46 | ) 47 | 48 | func init() { 49 | atomicLevel = zap.NewAtomicLevelAt(zap.InfoLevel) 50 | config := zap.Config{ 51 | Level: atomicLevel, 52 | Development: false, 53 | Encoding: "console", 54 | EncoderConfig: zap.NewProductionEncoderConfig(), 55 | OutputPaths: []string{"stdout"}, 56 | ErrorOutputPaths: []string{"stderr"}, 57 | } 58 | 59 | config.EncoderConfig.TimeKey = "timestamp" 60 | config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder 61 | config.EncoderConfig.LevelKey = "level" 62 | config.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder 63 | config.EncoderConfig.CallerKey = "caller" 64 | config.EncoderConfig.EncodeCaller = zapcore.ShortCallerEncoder 65 | config.EncoderConfig.MessageKey = "message" 66 | config.EncoderConfig.StacktraceKey = "" 67 | 68 | var err error 69 | log, err = config.Build() 70 | if err != nil { 71 | panic(err) 72 | } 73 | 74 | AppLog = log.Sugar().With("component", "AMF", "category", "App") 75 | InitLog = log.Sugar().With("component", "AMF", "category", "Init") 76 | CfgLog = log.Sugar().With("component", "AMF", "category", "CFG") 77 | ContextLog = log.Sugar().With("component", "AMF", "category", "Context") 78 | DataRepoLog = log.Sugar().With("component", "AMF", "category", "DBRepo") 79 | NgapLog = log.Sugar().With("component", "AMF", "category", "NGAP") 80 | HandlerLog = log.Sugar().With("component", "AMF", "category", "Handler") 81 | HttpLog = log.Sugar().With("component", "AMF", "category", "HTTP") 82 | GmmLog = log.Sugar().With("component", "AMF", "category", "GMM") 83 | MtLog = log.Sugar().With("component", "AMF", "category", "MT") 84 | ProducerLog = log.Sugar().With("component", "AMF", "category", "Producer") 85 | LocationLog = log.Sugar().With("component", "AMF", "category", "LocInfo") 86 | CommLog = log.Sugar().With("component", "AMF", "category", "Comm") 87 | CallbackLog = log.Sugar().With("component", "AMF", "category", "Callback") 88 | UtilLog = log.Sugar().With("component", "AMF", "category", "Util") 89 | NasLog = log.Sugar().With("component", "AMF", "category", "NAS") 90 | ConsumerLog = log.Sugar().With("component", "AMF", "category", "Consumer") 91 | EeLog = log.Sugar().With("component", "AMF", "category", "EventExposure") 92 | GinLog = log.Sugar().With("component", "AMF", "category", "GIN") 93 | GrpcLog = log.Sugar().With("component", "AMF", "category", "GRPC") 94 | KafkaLog = log.Sugar().With("component", "AMF", "category", "Kafka") 95 | } 96 | 97 | func GetLogger() *zap.Logger { 98 | return log 99 | } 100 | 101 | // SetLogLevel: set the log level (panic|fatal|error|warn|info|debug) 102 | func SetLogLevel(level zapcore.Level) { 103 | CfgLog.Infoln("set log level:", level) 104 | atomicLevel.SetLevel(level) 105 | } 106 | -------------------------------------------------------------------------------- /metrics/kafka.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022-present Intel Corporation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package metrics 6 | 7 | import ( 8 | "context" 9 | "encoding/json" 10 | "fmt" 11 | "time" 12 | 13 | "github.com/omec-project/amf/factory" 14 | "github.com/omec-project/amf/logger" 15 | mi "github.com/omec-project/util/metricinfo" 16 | "github.com/segmentio/kafka-go" 17 | ) 18 | 19 | type Writer struct { 20 | kafkaWriter *kafka.Writer 21 | } 22 | 23 | var StatWriter Writer 24 | 25 | func InitialiseKafkaStream(config *factory.Configuration) error { 26 | if !*factory.AmfConfig.Configuration.KafkaInfo.EnableKafka { 27 | logger.KafkaLog.Info("Kafka disabled") 28 | return nil 29 | } 30 | 31 | brokerUrl := "sd-core-kafka-headless:9092" 32 | topicName := "sdcore-data-source-amf" 33 | 34 | if config.KafkaInfo.BrokerUri != "" && config.KafkaInfo.BrokerPort != 0 { 35 | brokerUrl = fmt.Sprintf("%s:%d", config.KafkaInfo.BrokerUri, config.KafkaInfo.BrokerPort) 36 | } 37 | logger.KafkaLog.Debugf("initialise kafka broker url [%v]", brokerUrl) 38 | 39 | if config.KafkaInfo.Topic != "" { 40 | topicName = config.KafkaInfo.Topic 41 | } 42 | logger.KafkaLog.Debugf("initialise kafka Topic [%v]", config.KafkaInfo.Topic) 43 | 44 | producer := kafka.Writer{ 45 | Addr: kafka.TCP(brokerUrl), 46 | Topic: topicName, 47 | Balancer: &kafka.LeastBytes{}, 48 | BatchTimeout: 10 * time.Millisecond, 49 | } 50 | 51 | StatWriter = Writer{ 52 | kafkaWriter: &producer, 53 | } 54 | 55 | logger.KafkaLog.Debugf("initialising kafka stream with url[%v], topic[%v]", brokerUrl, topicName) 56 | return nil 57 | } 58 | 59 | func GetWriter() Writer { 60 | return StatWriter 61 | } 62 | 63 | func (writer Writer) SendMessage(message []byte) error { 64 | msg := kafka.Message{Value: message} 65 | err := writer.kafkaWriter.WriteMessages(context.Background(), msg) 66 | return err 67 | } 68 | 69 | func (writer Writer) PublishUeCtxtEvent(ctxt mi.CoreSubscriber, op mi.SubscriberOp) error { 70 | smKafkaEvt := mi.MetricEvent{ 71 | EventType: mi.CSubscriberEvt, 72 | SubscriberData: mi.CoreSubscriberData{Subscriber: ctxt, Operation: op}, 73 | } 74 | if msg, err := json.Marshal(smKafkaEvt); err != nil { 75 | logger.KafkaLog.Errorf("publishing ue context event error [%v] ", err.Error()) 76 | return err 77 | } else { 78 | logger.KafkaLog.Debugf("publishing ue context event[%s] ", msg) 79 | if err := StatWriter.SendMessage(msg); err != nil { 80 | logger.KafkaLog.Errorf("could not publish ue context event, error [%v]", err.Error()) 81 | } 82 | } 83 | return nil 84 | } 85 | 86 | func (writer Writer) PublishNfStatusEvent(msgEvent mi.MetricEvent) error { 87 | if msg, err := json.Marshal(msgEvent); err != nil { 88 | return err 89 | } else { 90 | logger.KafkaLog.Debugf("publishing nf status event[%s] ", msg) 91 | if err := StatWriter.SendMessage(msg); err != nil { 92 | logger.KafkaLog.Errorf("error publishing nf status event: %v", err) 93 | } 94 | } 95 | return nil 96 | } 97 | -------------------------------------------------------------------------------- /metrics/telemetry.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | /* 8 | * AMF Statistics exposing to promethus 9 | * 10 | */ 11 | 12 | package metrics 13 | 14 | import ( 15 | "net/http" 16 | 17 | "github.com/omec-project/amf/logger" 18 | "github.com/prometheus/client_golang/prometheus" 19 | "github.com/prometheus/client_golang/prometheus/promhttp" 20 | ) 21 | 22 | // AmfStats captures AMF level stats 23 | type AmfStats struct { 24 | ngapMsg *prometheus.CounterVec 25 | gnbSessionProfile *prometheus.GaugeVec 26 | } 27 | 28 | var amfStats *AmfStats 29 | 30 | func initAmfStats() *AmfStats { 31 | return &AmfStats{ 32 | ngapMsg: prometheus.NewCounterVec(prometheus.CounterOpts{ 33 | Name: "ngap_messages_total", 34 | Help: "ngap interface counters", 35 | }, []string{"amf_id", "msg_type", "direction", "result", "reason"}), 36 | 37 | gnbSessionProfile: prometheus.NewGaugeVec(prometheus.GaugeOpts{ 38 | Name: "gnb_session_profile", 39 | Help: "gNB session Profile", 40 | }, []string{"id", "ip", "state", "tac"}), 41 | } 42 | } 43 | 44 | func (ps *AmfStats) register() error { 45 | prometheus.Unregister(ps.ngapMsg) 46 | 47 | if err := prometheus.Register(ps.ngapMsg); err != nil { 48 | return err 49 | } 50 | if err := prometheus.Register(ps.gnbSessionProfile); err != nil { 51 | return err 52 | } 53 | return nil 54 | } 55 | 56 | func init() { 57 | amfStats = initAmfStats() 58 | 59 | if err := amfStats.register(); err != nil { 60 | logger.AppLog.Errorln("AMF Stats register failed", err) 61 | } 62 | } 63 | 64 | // InitMetrics initialises AMF stats 65 | func InitMetrics() { 66 | http.Handle("/metrics", promhttp.Handler()) 67 | if err := http.ListenAndServe(":9089", nil); err != nil { 68 | logger.InitLog.Errorf("could not open metrics port: %v", err) 69 | } 70 | } 71 | 72 | // IncrementNgapMsgStats increments message level stats 73 | func IncrementNgapMsgStats(amfID, msgType, direction, result, reason string) { 74 | amfStats.ngapMsg.WithLabelValues(amfID, msgType, direction, result, reason).Inc() 75 | } 76 | 77 | // SetGnbSessProfileStats maintains Session profile info 78 | func SetGnbSessProfileStats(id, ip, state, tac string, count uint64) { 79 | amfStats.gnbSessionProfile.WithLabelValues(id, ip, state, tac).Set(float64(count)) 80 | } 81 | -------------------------------------------------------------------------------- /msgtypes/ngapmsgtypes/ngapmsgtypes.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package ngapmsgtypes 8 | 9 | import ( 10 | "github.com/omec-project/ngap/ngapType" 11 | ) 12 | 13 | var NgapMsg map[int64]string 14 | 15 | func init() { 16 | BuildProcedureCodeToMsgMap() 17 | } 18 | 19 | func BuildProcedureCodeToMsgMap() { 20 | NgapMsg = make(map[int64]string, 255) 21 | NgapMsg[ngapType.ProcedureCodeAMFConfigurationUpdate] = "AMFConfigurationUpdate" 22 | NgapMsg[ngapType.ProcedureCodeAMFStatusIndication] = "AMFStatusIndication" 23 | NgapMsg[ngapType.ProcedureCodeCellTrafficTrace] = "CellTrafficTrace" 24 | NgapMsg[ngapType.ProcedureCodeDeactivateTrace] = "DeactivateTrace" 25 | NgapMsg[ngapType.ProcedureCodeDownlinkNASTransport] = "DownlinkNASTransport" 26 | NgapMsg[ngapType.ProcedureCodeDownlinkNonUEAssociatedNRPPaTransport] = "DownlinkNonUEAssociatedNRPPaTransport" 27 | NgapMsg[ngapType.ProcedureCodeDownlinkRANConfigurationTransfer] = "DownlinkRANConfigurationTransfer" 28 | NgapMsg[ngapType.ProcedureCodeDownlinkRANStatusTransfer] = "DownlinkRANStatusTransfer" 29 | NgapMsg[ngapType.ProcedureCodeDownlinkUEAssociatedNRPPaTransport] = "DownlinkUEAssociatedNRPPaTransport" 30 | NgapMsg[ngapType.ProcedureCodeErrorIndication] = "ErrorIndication" 31 | NgapMsg[ngapType.ProcedureCodeHandoverCancel] = "HandoverCancel" 32 | NgapMsg[ngapType.ProcedureCodeHandoverNotification] = "HandoverNotification" 33 | NgapMsg[ngapType.ProcedureCodeHandoverPreparation] = "HandoverPreparation" 34 | NgapMsg[ngapType.ProcedureCodeHandoverResourceAllocation] = "HandoverResourceAllocation" 35 | NgapMsg[ngapType.ProcedureCodeInitialContextSetup] = "InitialContextSetup" 36 | NgapMsg[ngapType.ProcedureCodeInitialUEMessage] = "InitialUEMessage" 37 | NgapMsg[ngapType.ProcedureCodeLocationReportingControl] = "LocationReportingControl" 38 | NgapMsg[ngapType.ProcedureCodeLocationReportingFailureIndication] = "LocationReportingFailureIndication" 39 | NgapMsg[ngapType.ProcedureCodeLocationReport] = "LocationReport" 40 | NgapMsg[ngapType.ProcedureCodeNASNonDeliveryIndication] = "NASNonDeliveryIndication" 41 | NgapMsg[ngapType.ProcedureCodeNGReset] = "NGReset" 42 | NgapMsg[ngapType.ProcedureCodeNGSetup] = "NGSetup" 43 | NgapMsg[ngapType.ProcedureCodeOverloadStart] = "OverloadStart" 44 | NgapMsg[ngapType.ProcedureCodeOverloadStop] = "OverloadStop" 45 | NgapMsg[ngapType.ProcedureCodePaging] = "Paging" 46 | NgapMsg[ngapType.ProcedureCodePathSwitchRequest] = "PathSwitchRequest" 47 | NgapMsg[ngapType.ProcedureCodePDUSessionResourceModify] = "PDUSessionResourceModify" 48 | NgapMsg[ngapType.ProcedureCodePDUSessionResourceModifyIndication] = "PDUSessionResourceModifyIndication" 49 | NgapMsg[ngapType.ProcedureCodePDUSessionResourceRelease] = "PDUSessionResourceRelease" 50 | NgapMsg[ngapType.ProcedureCodePDUSessionResourceSetup] = "PDUSessionResourceSetup" 51 | NgapMsg[ngapType.ProcedureCodePDUSessionResourceNotify] = "PDUSessionResourceNotify" 52 | NgapMsg[ngapType.ProcedureCodePrivateMessage] = "PrivateMessage" 53 | NgapMsg[ngapType.ProcedureCodePWSCancel] = "PWSCancel" 54 | NgapMsg[ngapType.ProcedureCodePWSFailureIndication] = "PWSFailureIndication" 55 | NgapMsg[ngapType.ProcedureCodePWSRestartIndication] = "PWSRestartIndication" 56 | NgapMsg[ngapType.ProcedureCodeRANConfigurationUpdate] = "RANConfigurationUpdate" 57 | NgapMsg[ngapType.ProcedureCodeRerouteNASRequest] = "RerouteNASRequest" 58 | NgapMsg[ngapType.ProcedureCodeRRCInactiveTransitionReport] = "RRCInactiveTransitionReport" 59 | NgapMsg[ngapType.ProcedureCodeTraceFailureIndication] = "TraceFailureIndication" 60 | NgapMsg[ngapType.ProcedureCodeTraceStart] = "TraceStart" 61 | NgapMsg[ngapType.ProcedureCodeUEContextModification] = "UEContextModification" 62 | NgapMsg[ngapType.ProcedureCodeUEContextRelease] = "UEContextRelease" 63 | NgapMsg[ngapType.ProcedureCodeUEContextReleaseRequest] = "UEContextReleaseRequest" 64 | NgapMsg[ngapType.ProcedureCodeUERadioCapabilityCheck] = "UERadioCapabilityCheck" 65 | NgapMsg[ngapType.ProcedureCodeUERadioCapabilityInfoIndication] = "UERadioCapabilityInfoIndication" 66 | NgapMsg[ngapType.ProcedureCodeUETNLABindingRelease] = "UETNLABindingRelease" 67 | NgapMsg[ngapType.ProcedureCodeUplinkNASTransport] = "UplinkNASTransport" 68 | NgapMsg[ngapType.ProcedureCodeUplinkNonUEAssociatedNRPPaTransport] = "NonUEAssociatedNRPPaTransport" 69 | NgapMsg[ngapType.ProcedureCodeUplinkRANConfigurationTransfer] = "RANConfigurationTransfer" 70 | NgapMsg[ngapType.ProcedureCodeUplinkRANStatusTransfer] = "RANStatusTransfer" 71 | NgapMsg[ngapType.ProcedureCodeUplinkUEAssociatedNRPPaTransport] = "UplinkUEAssociatedNRPPaTransport" 72 | NgapMsg[ngapType.ProcedureCodeWriteReplaceWarning] = "WriteReplaceWarning" 73 | NgapMsg[ngapType.ProcedureCodeSecondaryRATDataUsageReport] = "SecondaryRATDataUsageReport" 74 | } 75 | -------------------------------------------------------------------------------- /mt/api_ue_context_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_MT 8 | * 9 | * AMF Mobile Termination Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package mt 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | "github.com/omec-project/amf/producer" 23 | "github.com/omec-project/openapi" 24 | "github.com/omec-project/openapi/models" 25 | "github.com/omec-project/util/httpwrapper" 26 | ) 27 | 28 | // ProvideDomainSelectionInfo - Namf_MT Provide Domain Selection Info service Operation 29 | func HTTPProvideDomainSelectionInfo(c *gin.Context) { 30 | req := httpwrapper.NewRequest(c.Request, nil) 31 | req.Params["ueContextId"] = c.Params.ByName("ueContextId") 32 | infoClassQuery := c.Query("info-class") 33 | req.Query.Add("info-class", infoClassQuery) 34 | supportedFeaturesQuery := c.Query("supported-features") 35 | req.Query.Add("supported-features", supportedFeaturesQuery) 36 | 37 | rsp := producer.HandleProvideDomainSelectionInfoRequest(req) 38 | 39 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 40 | if err != nil { 41 | logger.MtLog.Errorln(err) 42 | problemDetails := models.ProblemDetails{ 43 | Status: http.StatusInternalServerError, 44 | Cause: "SYSTEM_FAILURE", 45 | Detail: err.Error(), 46 | } 47 | c.JSON(http.StatusInternalServerError, problemDetails) 48 | } else { 49 | c.Data(rsp.Status, "application/json", responseBody) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /mt/api_ue_reach_ind_document.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_MT 8 | * 9 | * AMF Mobile Termination Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package mt 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/gin-gonic/gin" 21 | "github.com/omec-project/amf/logger" 22 | ) 23 | 24 | // EnableUeReachability - Namf_MT EnableUEReachability service Operation 25 | func HTTPEnableUeReachability(c *gin.Context) { 26 | logger.MtLog.Warnf("Handle Enable Ue Reachability is not implemented.") 27 | c.JSON(http.StatusOK, gin.H{}) 28 | } 29 | -------------------------------------------------------------------------------- /mt/routers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | /* 7 | * Namf_MT 8 | * 9 | * AMF Mobile Termination Service 10 | * 11 | * API version: 1.0.0 12 | * Generated by: OpenAPI Generator (https://openapi-generator.tech) 13 | */ 14 | 15 | package mt 16 | 17 | import ( 18 | "net/http" 19 | "strings" 20 | 21 | "github.com/gin-gonic/gin" 22 | "github.com/omec-project/amf/logger" 23 | utilLogger "github.com/omec-project/util/logger" 24 | ) 25 | 26 | // Route is the information for every URI. 27 | type Route struct { 28 | // Name is the name of this Route. 29 | Name string 30 | // Method is the string for the HTTP method. ex) GET, POST etc.. 31 | Method string 32 | // Pattern is the pattern of the URI. 33 | Pattern string 34 | // HandlerFunc is the handler function of this route. 35 | HandlerFunc gin.HandlerFunc 36 | } 37 | 38 | // Routes is the list of the generated Route. 39 | type Routes []Route 40 | 41 | // NewRouter returns a new router. 42 | func NewRouter() *gin.Engine { 43 | router := utilLogger.NewGinWithZap(logger.GinLog) 44 | AddService(router) 45 | return router 46 | } 47 | 48 | func AddService(engine *gin.Engine) *gin.RouterGroup { 49 | group := engine.Group("/namf-mt/v1") 50 | 51 | for _, route := range routes { 52 | switch route.Method { 53 | case "GET": 54 | group.GET(route.Pattern, route.HandlerFunc) 55 | case "POST": 56 | group.POST(route.Pattern, route.HandlerFunc) 57 | case "PUT": 58 | group.PUT(route.Pattern, route.HandlerFunc) 59 | case "DELETE": 60 | group.DELETE(route.Pattern, route.HandlerFunc) 61 | case "PATCH": 62 | group.PATCH(route.Pattern, route.HandlerFunc) 63 | } 64 | } 65 | return group 66 | } 67 | 68 | // Index is the index handler. 69 | func Index(c *gin.Context) { 70 | c.String(http.StatusOK, "Hello World!") 71 | } 72 | 73 | var routes = Routes{ 74 | { 75 | "Index", 76 | "GET", 77 | "/", 78 | Index, 79 | }, 80 | 81 | { 82 | "ProvideDomainSelectionInfo", 83 | strings.ToUpper("Get"), 84 | "/ue-contexts/:ueContextId", 85 | HTTPProvideDomainSelectionInfo, 86 | }, 87 | 88 | { 89 | "EnableUeReachability", 90 | strings.ToUpper("Post"), 91 | "/ue-contexts/:ueContextId/ue-reachind", 92 | HTTPEnableUeReachability, 93 | }, 94 | } 95 | -------------------------------------------------------------------------------- /nas/dispatch.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package nas 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | 12 | "github.com/omec-project/amf/context" 13 | "github.com/omec-project/amf/gmm" 14 | "github.com/omec-project/nas" 15 | "github.com/omec-project/openapi/models" 16 | "github.com/omec-project/util/fsm" 17 | ) 18 | 19 | func Dispatch(ue *context.AmfUe, accessType models.AccessType, procedureCode int64, msg *nas.Message) error { 20 | if msg.GmmMessage == nil { 21 | return errors.New("gmm message is nil") 22 | } 23 | 24 | if msg.GsmMessage != nil { 25 | return errors.New("GSM Message should include in GMM Message") 26 | } 27 | 28 | if ue.State[accessType] == nil { 29 | return fmt.Errorf("UE State is empty (accessType=%q). Can't send GSM Message", accessType) 30 | } 31 | 32 | return gmm.GmmFSM.SendEvent(ue.State[accessType], gmm.GmmMessageEvent, fsm.ArgsType{ 33 | gmm.ArgAmfUe: ue, 34 | gmm.ArgAccessType: accessType, 35 | gmm.ArgNASMessage: msg.GmmMessage, 36 | gmm.ArgProcedureCode: procedureCode, 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /nas/handler.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package nas 8 | 9 | import ( 10 | "os" 11 | 12 | "github.com/omec-project/amf/context" 13 | "github.com/omec-project/amf/logger" 14 | "github.com/omec-project/amf/nas/nas_security" 15 | "github.com/omec-project/amf/protos/sdcoreAmfServer" 16 | "github.com/omec-project/openapi/models" 17 | ) 18 | 19 | func HandleNAS(ue *context.RanUe, procedureCode int64, nasPdu []byte) { 20 | amfSelf := context.AMF_Self() 21 | 22 | if ue == nil { 23 | logger.NasLog.Errorln("RanUe is nil") 24 | return 25 | } 26 | 27 | if nasPdu == nil { 28 | ue.Log.Errorln("nasPdu is nil") 29 | return 30 | } 31 | 32 | if ue.AmfUe == nil { 33 | ue.AmfUe = nas_security.FetchUeContextWithMobileIdentity(nasPdu) 34 | if ue.AmfUe == nil { 35 | ue.AmfUe = amfSelf.NewAmfUe("") 36 | } else { 37 | if amfSelf.EnableSctpLb && amfSelf.EnableDbStore { 38 | /* checking the guti-ue belongs to this amf instance */ 39 | id, err := amfSelf.Drsm.FindOwnerInt32ID(ue.AmfUe.Tmsi) 40 | if err != nil { 41 | logger.NasLog.Errorf("error checking guti-ue: %v", err) 42 | } 43 | if id != nil && id.PodName != os.Getenv("HOSTNAME") { 44 | rsp := &sdcoreAmfServer.AmfMessage{} 45 | rsp.VerboseMsg = "Redirecting Msg From AMF Pod !" 46 | rsp.Msgtype = sdcoreAmfServer.MsgType_REDIRECT_MSG 47 | rsp.AmfId = os.Getenv("HOSTNAME") 48 | /* TODO for this release setting pod ip to simplify logic in sctplb */ 49 | rsp.RedirectId = id.PodIp 50 | rsp.GnbId = ue.Ran.GnbId 51 | rsp.Msg = ue.SctplbMsg 52 | if ue.AmfUe != nil { 53 | ue.AmfUe.Remove() 54 | } else { 55 | if err := ue.Remove(); err != nil { 56 | logger.NasLog.Errorf("error removing ue: %v", err) 57 | } 58 | } 59 | ue.Ran.Amf2RanMsgChan <- rsp 60 | return 61 | } 62 | } 63 | } 64 | 65 | ue.AmfUe.Mutex.Lock() 66 | defer ue.AmfUe.Mutex.Unlock() 67 | 68 | ue.Log.Infoln("Antype from new RanUe:", ue.Ran.AnType) 69 | // AnType is set in SetRanId function. This is called 70 | // when we handle NGSetup. In case of sctplb enabled, 71 | // we dont call this function when AMF restarts. So we 72 | // need to set the AnType from stored Information. 73 | if amfSelf.EnableSctpLb { 74 | ue.Ran.AnType = models.AccessType__3_GPP_ACCESS 75 | } 76 | ue.AmfUe.AttachRanUe(ue) 77 | 78 | if ue.AmfUe.EventChannel == nil { 79 | ue.AmfUe.EventChannel = ue.AmfUe.NewEventChannel() 80 | ue.AmfUe.EventChannel.UpdateNasHandler(DispatchMsg) 81 | go ue.AmfUe.EventChannel.Start() 82 | } 83 | ue.AmfUe.EventChannel.UpdateNasHandler(DispatchMsg) 84 | 85 | nasMsg := context.NasMsg{ 86 | AnType: ue.Ran.AnType, 87 | NasMsg: nasPdu, 88 | ProcedureCode: procedureCode, 89 | } 90 | ue.AmfUe.EventChannel.SubmitMessage(nasMsg) 91 | 92 | return 93 | } 94 | if amfSelf.EnableSctpLb { 95 | ue.Ran.AnType = models.AccessType__3_GPP_ACCESS 96 | } 97 | 98 | msg, err := nas_security.Decode(ue.AmfUe, ue.Ran.AnType, nasPdu) 99 | if err != nil { 100 | ue.AmfUe.NASLog.Errorln(err) 101 | return 102 | } 103 | if err := Dispatch(ue.AmfUe, ue.Ran.AnType, procedureCode, msg); err != nil { 104 | ue.AmfUe.NASLog.Errorf("handle NAS Error: %v", err) 105 | } 106 | } 107 | 108 | func DispatchMsg(amfUe *context.AmfUe, transInfo context.NasMsg) { 109 | amfUe.NASLog.Infoln("handle Nas Message") 110 | msg, err := nas_security.Decode(amfUe, transInfo.AnType, transInfo.NasMsg) 111 | if err != nil { 112 | amfUe.NASLog.Errorln(err) 113 | return 114 | } 115 | 116 | if err := Dispatch(amfUe, transInfo.AnType, transInfo.ProcedureCode, msg); err != nil { 117 | amfUe.NASLog.Errorf("handle NAS Error: %v", err) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /ngap/ngap_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | package ngap_test 5 | 6 | import ( 7 | "testing" 8 | "time" 9 | 10 | "github.com/omec-project/amf/context" 11 | "github.com/omec-project/amf/factory" 12 | "github.com/omec-project/amf/logger" 13 | "github.com/omec-project/amf/metrics" 14 | "github.com/omec-project/amf/ngap" 15 | ngaputil "github.com/omec-project/amf/ngap/util" 16 | "github.com/omec-project/amf/util" 17 | ) 18 | 19 | func init() { 20 | // Initializing AMF Context from config. 21 | testAmfConfig := "../amfTest/amfcfg.yaml" 22 | if err := factory.InitConfigFactory(testAmfConfig); err != nil { 23 | logger.NgapLog.Fatalln("failed to initialize Factory Config") 24 | } 25 | if err := metrics.InitialiseKafkaStream(factory.AmfConfig.Configuration); err != nil { 26 | logger.NgapLog.Fatalln("failed to initialize Kafka Stream") 27 | } 28 | 29 | util.InitAmfContext(context.AMF_Self()) 30 | } 31 | 32 | // TestHandleNGSetupRequest validates package ngap's handling for NGSetupRequest 33 | func TestHandleNGSetupRequest(t *testing.T) { 34 | // test cases 35 | testTable := []struct { 36 | gnbName, tac string 37 | gnbId []byte 38 | bitLength uint64 39 | want, testId byte 40 | }{ 41 | // expecting SuccessfulOutcome 42 | { 43 | testId: 1, 44 | gnbName: "GNB2", 45 | tac: "\x00\x00\x01", 46 | gnbId: []byte{0x00, 0x00, 0x08}, 47 | bitLength: 22, 48 | want: ngaputil.NgapPDUSuccessfulOutcome, 49 | }, 50 | // expecting UnsuccessfulOutcome due to unsupported TA 51 | { 52 | testId: 2, 53 | gnbName: "GNB2", 54 | tac: "\x00\x00\x04", 55 | gnbId: []byte{0x00, 0x00, 0x08}, 56 | bitLength: 22, 57 | want: ngaputil.NgapPDUUnSuccessfulOutcome, 58 | }, 59 | } 60 | 61 | conn := &ngaputil.TestConn{} 62 | for _, test := range testTable { 63 | testNGSetupReq, err := ngaputil.GetNGSetupRequest(test.gnbId, test.bitLength, test.gnbName, test.tac) 64 | if err != nil { 65 | t.Log("Failed to to create NGSetupRequest") 66 | return 67 | } 68 | ngap.Dispatch(conn, testNGSetupReq) 69 | time.Sleep(2 * time.Second) 70 | // conn.data holds the NGAP response message 71 | if len(conn.Data) == 0 { 72 | t.Error("Unexpected message drop") 73 | return 74 | } 75 | 76 | // The first byte of the NGAPPDU indicates the type of NGAP Message 77 | if conn.Data[0] != test.want { 78 | t.Error("Test case", test.testId, "failed. Want:", 79 | ngaputil.MessageTypeMap[test.want], ", Got:", ngaputil.MessageTypeMap[conn.Data[0]]) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /oam/api_purge_ue_context.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Open Networking Foundation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package oam 7 | 8 | import ( 9 | "net/http" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/omec-project/amf/context" 13 | "github.com/omec-project/amf/logger" 14 | "github.com/omec-project/amf/producer" 15 | "github.com/omec-project/openapi/models" 16 | "github.com/omec-project/util/httpwrapper" 17 | ) 18 | 19 | func HTTPPurgeUEContext(c *gin.Context) { 20 | setCorsHeader(c) 21 | 22 | amfSelf := context.AMF_Self() 23 | req := httpwrapper.NewRequest(c.Request, nil) 24 | if supi, exists := c.Params.Get("supi"); exists { 25 | req.Params["supi"] = supi 26 | reqUri := req.URL.RequestURI() 27 | if ue, ok := amfSelf.AmfUeFindBySupi(supi); ok { 28 | sbiMsg := context.SbiMsg{ 29 | UeContextId: ue.Supi, 30 | ReqUri: reqUri, 31 | Msg: nil, 32 | Result: make(chan context.SbiResponseMsg, 10), 33 | } 34 | ue.EventChannel.UpdateSbiHandler(producer.HandleOAMPurgeUEContextRequest) 35 | ue.EventChannel.SubmitMessage(sbiMsg) 36 | msg := <-sbiMsg.Result 37 | if msg.ProblemDetails != nil { 38 | c.JSON(int(msg.ProblemDetails.(models.ProblemDetails).Status), msg.ProblemDetails) 39 | } else { 40 | c.JSON(http.StatusOK, nil) 41 | } 42 | } else { 43 | logger.ProducerLog.Errorln("No Ue found by the provided supi") 44 | c.JSON(http.StatusNotFound, nil) 45 | } 46 | } 47 | } 48 | 49 | func HTTPAmfInstanceDown(c *gin.Context) { 50 | setCorsHeader(c) 51 | 52 | nfId, _ := c.Params.Get("nfid") 53 | logger.ProducerLog.Infof("AMF Instance Down Notification from NRF: %v", nfId) 54 | req := httpwrapper.NewRequest(c.Request, nil) 55 | if nfInstanceId, exists := c.Params.Get("nfid"); exists { 56 | req.Params["nfid"] = nfInstanceId 57 | self := context.AMF_Self() 58 | if self.EnableDbStore { 59 | self.Drsm.DeletePod(nfInstanceId) 60 | } 61 | c.JSON(http.StatusOK, nil) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /oam/api_registered_ue_context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package oam 7 | 8 | import ( 9 | "net/http" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/omec-project/amf/logger" 13 | "github.com/omec-project/amf/producer" 14 | "github.com/omec-project/openapi" 15 | "github.com/omec-project/openapi/models" 16 | "github.com/omec-project/util/httpwrapper" 17 | ) 18 | 19 | func setCorsHeader(c *gin.Context) { 20 | c.Writer.Header().Set("Access-Control-Allow-Origin", "*") 21 | c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") 22 | c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") 23 | c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, PATCH, DELETE") 24 | } 25 | 26 | func HTTPRegisteredUEContext(c *gin.Context) { 27 | setCorsHeader(c) 28 | 29 | req := httpwrapper.NewRequest(c.Request, nil) 30 | if supi, exists := c.Params.Get("supi"); exists { 31 | req.Params["supi"] = supi 32 | } 33 | 34 | rsp := producer.HandleOAMRegisteredUEContext(req) 35 | 36 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 37 | if err != nil { 38 | logger.MtLog.Errorln(err) 39 | problemDetails := models.ProblemDetails{ 40 | Status: http.StatusInternalServerError, 41 | Cause: "SYSTEM_FAILURE", 42 | Detail: err.Error(), 43 | } 44 | c.JSON(http.StatusInternalServerError, problemDetails) 45 | } else { 46 | c.Data(rsp.Status, "application/json", responseBody) 47 | } 48 | } 49 | 50 | func HTTPGetActiveUes(c *gin.Context) { 51 | setCorsHeader(c) 52 | 53 | req := httpwrapper.NewRequest(c.Request, nil) 54 | 55 | rsp := producer.HandleOAMActiveUEContextsFromDB(req) 56 | 57 | responseBody, err := openapi.Serialize(rsp.Body, "application/json") 58 | if err != nil { 59 | logger.MtLog.Errorln(err) 60 | problemDetails := models.ProblemDetails{ 61 | Status: http.StatusInternalServerError, 62 | Cause: "SYSTEM_FAILURE", 63 | Detail: err.Error(), 64 | } 65 | c.JSON(http.StatusInternalServerError, problemDetails) 66 | } else { 67 | c.Data(rsp.Status, "application/json", responseBody) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /oam/routers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package oam 7 | 8 | import ( 9 | "net/http" 10 | "strings" 11 | 12 | "github.com/gin-contrib/cors" 13 | "github.com/gin-gonic/gin" 14 | "github.com/omec-project/amf/logger" 15 | utilLogger "github.com/omec-project/util/logger" 16 | ) 17 | 18 | // Route is the information for every URI. 19 | type Route struct { 20 | // Name is the name of this Route. 21 | Name string 22 | // Method is the string for the HTTP method. ex) GET, POST etc.. 23 | Method string 24 | // Pattern is the pattern of the URI. 25 | Pattern string 26 | // HandlerFunc is the handler function of this route. 27 | HandlerFunc gin.HandlerFunc 28 | } 29 | 30 | // Routes is the list of the generated Route. 31 | type Routes []Route 32 | 33 | // NewRouter returns a new router. 34 | func NewRouter() *gin.Engine { 35 | router := utilLogger.NewGinWithZap(logger.GinLog) 36 | AddService(router) 37 | 38 | router.Use(cors.New(cors.Config{ 39 | AllowMethods: []string{"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE"}, 40 | AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "User-Agent", "Referrer", "Host", "Token", "X-Requested-With"}, 41 | ExposeHeaders: []string{"Content-Length"}, 42 | AllowCredentials: true, 43 | AllowAllOrigins: true, 44 | MaxAge: 86400, 45 | })) 46 | 47 | return router 48 | } 49 | 50 | func AddService(engine *gin.Engine) *gin.RouterGroup { 51 | group := engine.Group("/namf-oam/v1") 52 | 53 | for _, route := range routes { 54 | switch route.Method { 55 | case "GET": 56 | group.GET(route.Pattern, route.HandlerFunc) 57 | case "DELETE": 58 | group.DELETE(route.Pattern, route.HandlerFunc) 59 | case "POST": 60 | group.POST(route.Pattern, route.HandlerFunc) 61 | } 62 | } 63 | return group 64 | } 65 | 66 | // Index is the index handler. 67 | func Index(c *gin.Context) { 68 | c.String(http.StatusOK, "Hello World!") 69 | } 70 | 71 | var routes = Routes{ 72 | { 73 | "Index", 74 | "GET", 75 | "/", 76 | Index, 77 | }, 78 | { 79 | "Registered UE Context", 80 | "GET", 81 | "/registered-ue-context", 82 | HTTPRegisteredUEContext, 83 | }, 84 | 85 | { 86 | "Individual Registered UE Context", 87 | "GET", 88 | "/registered-ue-context/:supi", 89 | HTTPRegisteredUEContext, 90 | }, 91 | 92 | { 93 | "Purge UE Context", 94 | strings.ToUpper("Delete"), 95 | "/purge-ue-context/:supi", 96 | HTTPPurgeUEContext, 97 | }, 98 | { 99 | "Active UE List", 100 | strings.ToUpper("get"), 101 | "/active-ues", 102 | HTTPGetActiveUes, 103 | }, 104 | { 105 | "Amf Instance Down Notification", 106 | strings.ToUpper("post"), 107 | "/amfInstanceDown/:nfid", 108 | HTTPAmfInstanceDown, 109 | }, 110 | } 111 | -------------------------------------------------------------------------------- /producer/callback/subscription.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Intel Corporation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package callback 8 | 9 | import ( 10 | "context" 11 | "reflect" 12 | 13 | amf_context "github.com/omec-project/amf/context" 14 | "github.com/omec-project/amf/logger" 15 | "github.com/omec-project/openapi/Namf_Communication" 16 | "github.com/omec-project/openapi/models" 17 | ) 18 | 19 | func SendAmfStatusChangeNotify(amfStatus string, guamiList []models.Guami) { 20 | amfSelf := amf_context.AMF_Self() 21 | 22 | amfSelf.AMFStatusSubscriptions.Range(func(key, value interface{}) bool { 23 | subscriptionData := value.(models.SubscriptionData) 24 | 25 | configuration := Namf_Communication.NewConfiguration() 26 | client := Namf_Communication.NewAPIClient(configuration) 27 | amfStatusNotification := models.AmfStatusChangeNotification{} 28 | amfStatusInfo := models.AmfStatusInfo{} 29 | 30 | for _, guami := range guamiList { 31 | for _, subGumi := range subscriptionData.GuamiList { 32 | if reflect.DeepEqual(guami, subGumi) { 33 | // AMF status is available 34 | amfStatusInfo.GuamiList = append(amfStatusInfo.GuamiList, guami) 35 | } 36 | } 37 | } 38 | 39 | amfStatusInfo = models.AmfStatusInfo{ 40 | StatusChange: (models.StatusChange)(amfStatus), 41 | TargetAmfRemoval: "", 42 | TargetAmfFailure: "", 43 | } 44 | 45 | amfStatusNotification.AmfStatusInfoList = append(amfStatusNotification.AmfStatusInfoList, amfStatusInfo) 46 | uri := subscriptionData.AmfStatusUri 47 | 48 | logger.ProducerLog.Infof("[AMF] Send Amf Status Change Notify to %s", uri) 49 | httpResponse, err := client.AmfStatusChangeCallbackDocumentApiServiceCallbackDocumentApi. 50 | AmfStatusChangeNotify(context.Background(), uri, amfStatusNotification) 51 | if err != nil { 52 | if httpResponse == nil { 53 | logger.HttpLog.Errorln(err.Error()) 54 | } else if err.Error() != httpResponse.Status { 55 | logger.HttpLog.Errorln(err.Error()) 56 | } 57 | } 58 | return true 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /producer/callback/ue_context.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Intel Corporation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package callback 8 | 9 | import ( 10 | "context" 11 | "fmt" 12 | 13 | amf_context "github.com/omec-project/amf/context" 14 | "github.com/omec-project/amf/logger" 15 | "github.com/omec-project/openapi/Namf_Communication" 16 | "github.com/omec-project/openapi/models" 17 | ) 18 | 19 | func SendN2InfoNotifyN2Handover(ue *amf_context.AmfUe, releaseList []int32) error { 20 | if ue.HandoverNotifyUri == "" { 21 | return fmt.Errorf("N2 Info Notify N2Handover failed(uri dose not exist)") 22 | } 23 | configuration := Namf_Communication.NewConfiguration() 24 | client := Namf_Communication.NewAPIClient(configuration) 25 | 26 | n2InformationNotification := models.N2InformationNotification{ 27 | N2NotifySubscriptionId: ue.Supi, 28 | ToReleaseSessionList: releaseList, 29 | NotifyReason: models.N2InfoNotifyReason_HANDOVER_COMPLETED, 30 | } 31 | 32 | _, httpResponse, err := client.N2MessageNotifyCallbackDocumentApiServiceCallbackDocumentApi. 33 | N2InfoNotify(context.Background(), ue.HandoverNotifyUri, n2InformationNotification) 34 | 35 | if err == nil { 36 | // TODO: handle Msg 37 | } else { 38 | if httpResponse == nil { 39 | logger.HttpLog.Errorln(err.Error()) 40 | } else if err.Error() != httpResponse.Status { 41 | logger.HttpLog.Errorln(err.Error()) 42 | } 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /producer/location_info.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package producer 8 | 9 | import ( 10 | "net/http" 11 | 12 | "github.com/omec-project/amf/context" 13 | "github.com/omec-project/amf/logger" 14 | "github.com/omec-project/openapi/models" 15 | "github.com/omec-project/util/httpwrapper" 16 | ) 17 | 18 | func LocationInfoHandler(s1, s2 string, msg interface{}) (interface{}, string, interface{}, interface{}) { 19 | switch msg := msg.(type) { 20 | case models.RequestLocInfo: 21 | r1, r2 := ProvideLocationInfoProcedure(msg, s1) 22 | return r1, "", r2, nil 23 | } 24 | 25 | return nil, "", nil, nil 26 | } 27 | 28 | func HandleProvideLocationInfoRequest(request *httpwrapper.Request) *httpwrapper.Response { 29 | var ue *context.AmfUe 30 | var ok bool 31 | logger.ProducerLog.Info("Handle Provide Location Info Request") 32 | 33 | requestLocInfo := request.Body.(models.RequestLocInfo) 34 | ueContextID := request.Params["ueContextId"] 35 | 36 | amfSelf := context.AMF_Self() 37 | if ue, ok = amfSelf.AmfUeFindByUeContextID(ueContextID); !ok { 38 | problemDetails := &models.ProblemDetails{ 39 | Status: http.StatusNotFound, 40 | Cause: "CONTEXT_NOT_FOUND", 41 | } 42 | return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) 43 | } 44 | 45 | sbiMsg := context.SbiMsg{ 46 | UeContextId: ueContextID, 47 | ReqUri: "", 48 | Msg: requestLocInfo, 49 | Result: make(chan context.SbiResponseMsg, 10), 50 | } 51 | var provideLocInfo *models.ProvideLocInfo 52 | ue.EventChannel.UpdateSbiHandler(LocationInfoHandler) 53 | ue.EventChannel.SubmitMessage(sbiMsg) 54 | msg := <-sbiMsg.Result 55 | if msg.RespData != nil { 56 | provideLocInfo = msg.RespData.(*models.ProvideLocInfo) 57 | } 58 | // provideLocInfo, problemDetails := ProvideLocationInfoProcedure(requestLocInfo, ueContextID) 59 | if msg.ProblemDetails != nil { 60 | return httpwrapper.NewResponse(int(msg.ProblemDetails.(*models.ProblemDetails).Status), nil, msg.ProblemDetails.(*models.ProblemDetails)) 61 | } else { 62 | return httpwrapper.NewResponse(http.StatusOK, nil, provideLocInfo) 63 | } 64 | } 65 | 66 | func ProvideLocationInfoProcedure(requestLocInfo models.RequestLocInfo, ueContextID string) ( 67 | *models.ProvideLocInfo, *models.ProblemDetails, 68 | ) { 69 | amfSelf := context.AMF_Self() 70 | 71 | ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) 72 | if !ok { 73 | problemDetails := &models.ProblemDetails{ 74 | Status: http.StatusNotFound, 75 | Cause: "CONTEXT_NOT_FOUND", 76 | } 77 | return nil, problemDetails 78 | } 79 | 80 | anType := ue.GetAnType() 81 | if anType == "" { 82 | problemDetails := &models.ProblemDetails{ 83 | Status: http.StatusNotFound, 84 | Cause: "CONTEXT_NOT_FOUND", 85 | } 86 | return nil, problemDetails 87 | } 88 | 89 | provideLocInfo := new(models.ProvideLocInfo) 90 | 91 | ranUe := ue.RanUe[anType] 92 | if requestLocInfo.Req5gsLoc || requestLocInfo.ReqCurrentLoc { 93 | provideLocInfo.CurrentLoc = true 94 | provideLocInfo.Location = &ue.Location 95 | } 96 | 97 | if requestLocInfo.ReqRatType { 98 | provideLocInfo.RatType = ue.RatType 99 | } 100 | 101 | if requestLocInfo.ReqTimeZone { 102 | provideLocInfo.Timezone = ue.TimeZone 103 | } 104 | 105 | if requestLocInfo.SupportedFeatures != "" { 106 | provideLocInfo.SupportedFeatures = ranUe.SupportedFeatures 107 | } 108 | return provideLocInfo, nil 109 | } 110 | -------------------------------------------------------------------------------- /producer/mt.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package producer 8 | 9 | import ( 10 | "net/http" 11 | 12 | "github.com/omec-project/amf/context" 13 | "github.com/omec-project/amf/logger" 14 | "github.com/omec-project/openapi/models" 15 | "github.com/omec-project/util/httpwrapper" 16 | ) 17 | 18 | func MtHandler(s1, s2 string, msg interface{}) (interface{}, string, interface{}, interface{}) { 19 | switch msg := msg.(type) { 20 | case string: 21 | r1, r2 := ProvideDomainSelectionInfoProcedure(s1, s2, msg) 22 | return r1, "", r2, nil 23 | } 24 | 25 | return nil, "", nil, nil 26 | } 27 | 28 | func HandleProvideDomainSelectionInfoRequest(request *httpwrapper.Request) *httpwrapper.Response { 29 | var ue *context.AmfUe 30 | var ok bool 31 | logger.MtLog.Info("Handle Provide Domain Selection Info Request") 32 | 33 | ueContextID := request.Params["ueContextId"] 34 | infoClassQuery := request.Query.Get("info-class") 35 | supportedFeaturesQuery := request.Query.Get("supported-features") 36 | 37 | amfSelf := context.AMF_Self() 38 | 39 | if ue, ok = amfSelf.AmfUeFindByUeContextID(ueContextID); !ok { 40 | problemDetails := &models.ProblemDetails{ 41 | Status: http.StatusNotFound, 42 | Cause: "CONTEXT_NOT_FOUND", 43 | } 44 | return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) 45 | } 46 | sbiMsg := context.SbiMsg{ 47 | UeContextId: ueContextID, 48 | ReqUri: infoClassQuery, 49 | Msg: supportedFeaturesQuery, 50 | Result: make(chan context.SbiResponseMsg, 10), 51 | } 52 | var ueContextInfo *models.UeContextInfo 53 | ue.EventChannel.UpdateSbiHandler(MtHandler) 54 | ue.EventChannel.SubmitMessage(sbiMsg) 55 | msg := <-sbiMsg.Result 56 | if msg.RespData != nil { 57 | ueContextInfo = msg.RespData.(*models.UeContextInfo) 58 | } 59 | // ueContextInfo, problemDetails := ProvideDomainSelectionInfoProcedure(ueContextID, 60 | // infoClassQuery, supportedFeaturesQuery) 61 | if msg.ProblemDetails != nil { 62 | return httpwrapper.NewResponse(int(msg.ProblemDetails.(models.ProblemDetails).Status), nil, msg.ProblemDetails.(models.ProblemDetails)) 63 | } else { 64 | return httpwrapper.NewResponse(http.StatusOK, nil, ueContextInfo) 65 | } 66 | } 67 | 68 | func ProvideDomainSelectionInfoProcedure(ueContextID string, infoClassQuery string, supportedFeaturesQuery string) ( 69 | *models.UeContextInfo, *models.ProblemDetails, 70 | ) { 71 | amfSelf := context.AMF_Self() 72 | 73 | ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) 74 | if !ok { 75 | problemDetails := &models.ProblemDetails{ 76 | Status: http.StatusNotFound, 77 | Cause: "CONTEXT_NOT_FOUND", 78 | } 79 | return nil, problemDetails 80 | } 81 | 82 | ueContextInfo := new(models.UeContextInfo) 83 | 84 | // TODO: Error Status 307, 403 in TS29.518 Table 6.3.3.3.3.1-3 85 | anType := ue.GetAnType() 86 | if anType != "" && infoClassQuery != "" { 87 | ranUe := ue.RanUe[anType] 88 | ueContextInfo.AccessType = anType 89 | ueContextInfo.LastActTime = ranUe.LastActTime 90 | ueContextInfo.RatType = ue.RatType 91 | ueContextInfo.SupportedFeatures = ranUe.SupportedFeatures 92 | ueContextInfo.SupportVoPS = ranUe.SupportVoPS 93 | ueContextInfo.SupportVoPSn3gpp = ranUe.SupportVoPSn3gpp 94 | } 95 | 96 | return ueContextInfo, nil 97 | } 98 | -------------------------------------------------------------------------------- /producer/oam_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Open Networking Foundation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package producer 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/omec-project/amf/context" 12 | "github.com/omec-project/amf/factory" 13 | "github.com/omec-project/amf/gmm" 14 | "github.com/omec-project/amf/logger" 15 | "github.com/omec-project/amf/util" 16 | "github.com/omec-project/openapi/models" 17 | "github.com/omec-project/util/fsm" 18 | "github.com/stretchr/testify/assert" 19 | ) 20 | 21 | func init() { 22 | if err := factory.InitConfigFactory("../amfTest/amfcfg.yaml"); err != nil { 23 | logger.ProducerLog.Errorf("error in InitConfigFactory: %v", err) 24 | } 25 | 26 | self := context.AMF_Self() 27 | util.InitAmfContext(self) 28 | 29 | gmm.Mockinit() 30 | } 31 | 32 | func TestHandleOAMPurgeUEContextRequest_UEDeregistered(t *testing.T) { 33 | self := context.AMF_Self() 34 | var err error 35 | self.Drsm, err = util.MockDrsmInit() 36 | if err != nil { 37 | logger.ProducerLog.Errorf("error in MockDrsmInit: %v", err) 38 | } 39 | amfUe := self.NewAmfUe("imsi-208930100007497") 40 | 41 | HandleOAMPurgeUEContextRequest(amfUe.Supi, "", nil) 42 | 43 | if _, ok := self.AmfUeFindBySupi(amfUe.Supi); ok { 44 | t.Errorf("test failed") 45 | } 46 | 47 | assert.Equal(t, uint32(0), gmm.MockDeregisteredInitiatedCallCount) 48 | assert.Equal(t, uint32(0), gmm.MockRegisteredCallCount) 49 | } 50 | 51 | func TestHandleOAMPurgeUEContextRequest_UERegistered(t *testing.T) { 52 | self := context.AMF_Self() 53 | amfUe := self.NewAmfUe("imsi-208930100007497") 54 | amfUe.State[models.AccessType__3_GPP_ACCESS] = fsm.NewState(context.Registered) 55 | 56 | HandleOAMPurgeUEContextRequest(amfUe.Supi, "", nil) 57 | 58 | if _, ok := self.AmfUeFindBySupi(amfUe.Supi); ok { 59 | t.Errorf("test failed") 60 | } 61 | 62 | assert.Equal(t, uint32(2), gmm.MockRegisteredCallCount) 63 | assert.Equal(t, uint32(1), gmm.MockDeregisteredInitiatedCallCount) 64 | } 65 | -------------------------------------------------------------------------------- /producer/subscription.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package producer 7 | 8 | import ( 9 | "net/http" 10 | "reflect" 11 | 12 | "github.com/omec-project/amf/context" 13 | "github.com/omec-project/amf/logger" 14 | "github.com/omec-project/openapi/models" 15 | "github.com/omec-project/util/httpwrapper" 16 | ) 17 | 18 | // TS 29.518 5.2.2.5.1 19 | func HandleAMFStatusChangeSubscribeRequest(request *httpwrapper.Request) *httpwrapper.Response { 20 | logger.CommLog.Info("Handle AMF Status Change Subscribe Request") 21 | 22 | subscriptionDataReq := request.Body.(models.SubscriptionData) 23 | 24 | subscriptionDataRsp, locationHeader, problemDetails := AMFStatusChangeSubscribeProcedure(subscriptionDataReq) 25 | if problemDetails != nil { 26 | return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) 27 | } 28 | 29 | headers := http.Header{ 30 | "Location": {locationHeader}, 31 | } 32 | return httpwrapper.NewResponse(http.StatusCreated, headers, subscriptionDataRsp) 33 | } 34 | 35 | func AMFStatusChangeSubscribeProcedure(subscriptionDataReq models.SubscriptionData) ( 36 | subscriptionDataRsp models.SubscriptionData, locationHeader string, problemDetails *models.ProblemDetails, 37 | ) { 38 | amfSelf := context.AMF_Self() 39 | 40 | for _, guami := range subscriptionDataReq.GuamiList { 41 | for _, servedGumi := range amfSelf.ServedGuamiList { 42 | if reflect.DeepEqual(guami, servedGumi) { 43 | // AMF status is available 44 | subscriptionDataRsp.GuamiList = append(subscriptionDataRsp.GuamiList, guami) 45 | } 46 | } 47 | } 48 | 49 | if subscriptionDataRsp.GuamiList != nil { 50 | newSubscriptionID := amfSelf.NewAMFStatusSubscription(subscriptionDataReq) 51 | locationHeader = subscriptionDataReq.AmfStatusUri + "/" + newSubscriptionID 52 | logger.CommLog.Infof("new AMF Status Subscription[%s]", newSubscriptionID) 53 | return 54 | } else { 55 | problemDetails = &models.ProblemDetails{ 56 | Status: http.StatusForbidden, 57 | Cause: "UNSPECIFIED", 58 | } 59 | return 60 | } 61 | } 62 | 63 | // TS 29.518 5.2.2.5.2 64 | func HandleAMFStatusChangeUnSubscribeRequest(request *httpwrapper.Request) *httpwrapper.Response { 65 | logger.CommLog.Info("Handle AMF Status Change UnSubscribe Request") 66 | 67 | subscriptionID := request.Params["subscriptionId"] 68 | 69 | problemDetails := AMFStatusChangeUnSubscribeProcedure(subscriptionID) 70 | if problemDetails != nil { 71 | return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) 72 | } else { 73 | return httpwrapper.NewResponse(http.StatusNoContent, nil, nil) 74 | } 75 | } 76 | 77 | func AMFStatusChangeUnSubscribeProcedure(subscriptionID string) (problemDetails *models.ProblemDetails) { 78 | amfSelf := context.AMF_Self() 79 | 80 | if _, ok := amfSelf.FindAMFStatusSubscription(subscriptionID); !ok { 81 | problemDetails = &models.ProblemDetails{ 82 | Status: http.StatusNotFound, 83 | Cause: "SUBSCRIPTION_NOT_FOUND", 84 | } 85 | } else { 86 | logger.CommLog.Debugf("Delete AMF status subscription[%s]", subscriptionID) 87 | amfSelf.DeleteAMFStatusSubscription(subscriptionID) 88 | } 89 | return 90 | } 91 | 92 | // TS 29.518 5.2.2.5.1.3 93 | func HandleAMFStatusChangeSubscribeModify(request *httpwrapper.Request) *httpwrapper.Response { 94 | logger.CommLog.Info("Handle AMF Status Change Subscribe Modify Request") 95 | 96 | updateSubscriptionData := request.Body.(models.SubscriptionData) 97 | subscriptionID := request.Params["subscriptionId"] 98 | 99 | updatedSubscriptionData, problemDetails := AMFStatusChangeSubscribeModifyProcedure(subscriptionID, 100 | updateSubscriptionData) 101 | if problemDetails != nil { 102 | return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) 103 | } else { 104 | return httpwrapper.NewResponse(http.StatusAccepted, nil, updatedSubscriptionData) 105 | } 106 | } 107 | 108 | func AMFStatusChangeSubscribeModifyProcedure(subscriptionID string, subscriptionData models.SubscriptionData) ( 109 | *models.SubscriptionData, *models.ProblemDetails, 110 | ) { 111 | amfSelf := context.AMF_Self() 112 | 113 | if currentSubscriptionData, ok := amfSelf.FindAMFStatusSubscription(subscriptionID); !ok { 114 | problemDetails := &models.ProblemDetails{ 115 | Status: http.StatusForbidden, 116 | Cause: "Forbidden", 117 | } 118 | return nil, problemDetails 119 | } else { 120 | logger.CommLog.Debugf("Modify AMF status subscription[%s]", subscriptionID) 121 | 122 | currentSubscriptionData.GuamiList = currentSubscriptionData.GuamiList[:0] 123 | 124 | currentSubscriptionData.GuamiList = append(currentSubscriptionData.GuamiList, subscriptionData.GuamiList...) 125 | currentSubscriptionData.AmfStatusUri = subscriptionData.AmfStatusUri 126 | 127 | amfSelf.AMFStatusSubscriptions.Store(subscriptionID, currentSubscriptionData) 128 | return currentSubscriptionData, nil 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /protos/sdcoreAmfServer/server_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022-present Intel Corporation 2 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 3 | // Copyright 2019 free5GC.org 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | // 7 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 8 | // versions: 9 | // - protoc-gen-go-grpc v1.2.0 10 | // - protoc v3.21.5 11 | // source: server.proto 12 | 13 | package sdcoreAmfServer 14 | 15 | import ( 16 | context "context" 17 | 18 | grpc "google.golang.org/grpc" 19 | codes "google.golang.org/grpc/codes" 20 | status "google.golang.org/grpc/status" 21 | ) 22 | 23 | // This is a compile-time assertion to ensure that this generated file 24 | // is compatible with the grpc package it is being compiled against. 25 | // Requires gRPC-Go v1.32.0 or later. 26 | const _ = grpc.SupportPackageIsVersion7 27 | 28 | // NgapServiceClient is the client API for NgapService service. 29 | // 30 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 31 | type NgapServiceClient interface { 32 | HandleMessage(ctx context.Context, opts ...grpc.CallOption) (NgapService_HandleMessageClient, error) 33 | } 34 | 35 | type ngapServiceClient struct { 36 | cc grpc.ClientConnInterface 37 | } 38 | 39 | func NewNgapServiceClient(cc grpc.ClientConnInterface) NgapServiceClient { 40 | return &ngapServiceClient{cc} 41 | } 42 | 43 | func (c *ngapServiceClient) HandleMessage(ctx context.Context, opts ...grpc.CallOption) (NgapService_HandleMessageClient, error) { 44 | stream, err := c.cc.NewStream(ctx, &NgapService_ServiceDesc.Streams[0], "/sdcoreAmfServer.NgapService/HandleMessage", opts...) 45 | if err != nil { 46 | return nil, err 47 | } 48 | x := &ngapServiceHandleMessageClient{stream} 49 | return x, nil 50 | } 51 | 52 | type NgapService_HandleMessageClient interface { 53 | Send(*SctplbMessage) error 54 | Recv() (*AmfMessage, error) 55 | grpc.ClientStream 56 | } 57 | 58 | type ngapServiceHandleMessageClient struct { 59 | grpc.ClientStream 60 | } 61 | 62 | func (x *ngapServiceHandleMessageClient) Send(m *SctplbMessage) error { 63 | return x.ClientStream.SendMsg(m) 64 | } 65 | 66 | func (x *ngapServiceHandleMessageClient) Recv() (*AmfMessage, error) { 67 | m := new(AmfMessage) 68 | if err := x.ClientStream.RecvMsg(m); err != nil { 69 | return nil, err 70 | } 71 | return m, nil 72 | } 73 | 74 | // NgapServiceServer is the server API for NgapService service. 75 | // All implementations must embed UnimplementedNgapServiceServer 76 | // for forward compatibility 77 | type NgapServiceServer interface { 78 | HandleMessage(NgapService_HandleMessageServer) error 79 | mustEmbedUnimplementedNgapServiceServer() 80 | } 81 | 82 | // UnimplementedNgapServiceServer must be embedded to have forward compatible implementations. 83 | type UnimplementedNgapServiceServer struct { 84 | } 85 | 86 | func (UnimplementedNgapServiceServer) HandleMessage(NgapService_HandleMessageServer) error { 87 | return status.Errorf(codes.Unimplemented, "method HandleMessage not implemented") 88 | } 89 | func (UnimplementedNgapServiceServer) mustEmbedUnimplementedNgapServiceServer() {} 90 | 91 | // UnsafeNgapServiceServer may be embedded to opt out of forward compatibility for this service. 92 | // Use of this interface is not recommended, as added methods to NgapServiceServer will 93 | // result in compilation errors. 94 | type UnsafeNgapServiceServer interface { 95 | mustEmbedUnimplementedNgapServiceServer() 96 | } 97 | 98 | func RegisterNgapServiceServer(s grpc.ServiceRegistrar, srv NgapServiceServer) { 99 | s.RegisterService(&NgapService_ServiceDesc, srv) 100 | } 101 | 102 | func _NgapService_HandleMessage_Handler(srv interface{}, stream grpc.ServerStream) error { 103 | return srv.(NgapServiceServer).HandleMessage(&ngapServiceHandleMessageServer{stream}) 104 | } 105 | 106 | type NgapService_HandleMessageServer interface { 107 | Send(*AmfMessage) error 108 | Recv() (*SctplbMessage, error) 109 | grpc.ServerStream 110 | } 111 | 112 | type ngapServiceHandleMessageServer struct { 113 | grpc.ServerStream 114 | } 115 | 116 | func (x *ngapServiceHandleMessageServer) Send(m *AmfMessage) error { 117 | return x.ServerStream.SendMsg(m) 118 | } 119 | 120 | func (x *ngapServiceHandleMessageServer) Recv() (*SctplbMessage, error) { 121 | m := new(SctplbMessage) 122 | if err := x.ServerStream.RecvMsg(m); err != nil { 123 | return nil, err 124 | } 125 | return m, nil 126 | } 127 | 128 | // NgapService_ServiceDesc is the grpc.ServiceDesc for NgapService service. 129 | // It's only intended for direct use with grpc.RegisterService, 130 | // and not to be introspected or modified (even as a copy) 131 | var NgapService_ServiceDesc = grpc.ServiceDesc{ 132 | ServiceName: "sdcoreAmfServer.NgapService", 133 | HandlerType: (*NgapServiceServer)(nil), 134 | Methods: []grpc.MethodDesc{}, 135 | Streams: []grpc.StreamDesc{ 136 | { 137 | StreamName: "HandleMessage", 138 | Handler: _NgapService_HandleMessage_Handler, 139 | ServerStreams: true, 140 | ClientStreams: true, 141 | }, 142 | }, 143 | Metadata: "server.proto", 144 | } 145 | -------------------------------------------------------------------------------- /protos/server.proto: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | syntax = "proto3"; 5 | package sdcoreAmfServer; 6 | option go_package = "./sdcoreAmfServer"; 7 | 8 | enum msgType { 9 | UNKNOWN = 0; 10 | INIT_MSG = 1; 11 | GNB_MSG = 2; 12 | AMF_MSG = 3; 13 | REDIRECT_MSG = 4; 14 | GNB_DISC = 5; 15 | GNB_CONN = 6; 16 | } 17 | 18 | message SctplbMessage { 19 | string SctplbId = 1; 20 | msgType Msgtype = 2; 21 | string GnbIpAddr = 3; 22 | string VerboseMsg = 4; 23 | bytes Msg = 5; 24 | string GnbId = 6; 25 | } 26 | 27 | message AmfMessage { 28 | string AmfId = 1; 29 | string RedirectId = 2; 30 | msgType Msgtype = 3; 31 | string GnbIpAddr = 4; 32 | string GnbId = 5; 33 | string VerboseMsg = 6; 34 | bytes Msg = 7; 35 | } 36 | 37 | service NgapService { 38 | rpc HandleMessage(stream SctplbMessage) returns (stream AmfMessage) {} 39 | } 40 | -------------------------------------------------------------------------------- /service/amf_server.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package service 7 | 8 | import ( 9 | "fmt" 10 | "net" 11 | "os" 12 | 13 | "github.com/omec-project/amf/context" 14 | "github.com/omec-project/amf/factory" 15 | "github.com/omec-project/amf/logger" 16 | "github.com/omec-project/amf/metrics" 17 | "github.com/omec-project/amf/ngap" 18 | "github.com/omec-project/amf/protos/sdcoreAmfServer" 19 | mi "github.com/omec-project/util/metricinfo" 20 | "google.golang.org/grpc" 21 | ) 22 | 23 | type Server struct { 24 | sdcoreAmfServer.UnimplementedNgapServiceServer 25 | } 26 | 27 | func (s *Server) HandleMessage(srv sdcoreAmfServer.NgapService_HandleMessageServer) error { 28 | Amf2RanMsgChan := make(chan *sdcoreAmfServer.AmfMessage, 100) 29 | 30 | go func() { 31 | for { 32 | msg1 := <-Amf2RanMsgChan 33 | logger.GrpcLog.Infof("send Response message body from client (%s): Verbose - %s, MsgType %v GnbId: %v", msg1.AmfId, msg1.VerboseMsg, msg1.Msgtype, msg1.GnbId) 34 | if err := srv.Send(msg1); err != nil { 35 | logger.GrpcLog.Errorln("error in sending response") 36 | } 37 | } 38 | }() 39 | 40 | for { 41 | req, err := srv.Recv() /* TODO : handle errors */ 42 | if err != nil { 43 | logger.GrpcLog.Errorln("error in SCTPLB stream", err) 44 | break 45 | } else { 46 | logger.GrpcLog.Debugf("receive message body from client (%s): GnbIp: %v, GnbId: %v, Verbose - %s, MsgType %v", req.SctplbId, req.GnbIpAddr, req.GnbId, req.VerboseMsg, req.Msgtype) 47 | switch req.Msgtype { 48 | case sdcoreAmfServer.MsgType_INIT_MSG: 49 | rsp := &sdcoreAmfServer.AmfMessage{} 50 | rsp.VerboseMsg = "Hello From AMF Pod !" 51 | rsp.Msgtype = sdcoreAmfServer.MsgType_INIT_MSG 52 | rsp.AmfId = os.Getenv("HOSTNAME") 53 | logger.GrpcLog.Debugf("send Response message body from client (%s): Verbose - %s, MsgType %v", rsp.AmfId, rsp.VerboseMsg, rsp.Msgtype) 54 | amfSelf := context.AMF_Self() 55 | var ran *context.AmfRan 56 | var ok bool 57 | if ran, ok = amfSelf.AmfRanFindByGnbId(req.GnbId); !ok { 58 | ran = amfSelf.NewAmfRanId(req.GnbId) 59 | if req.GnbId != "" { 60 | ran.GnbId = req.GnbId 61 | ran.RanId = ran.ConvertGnbIdToRanId(ran.GnbId) 62 | logger.GrpcLog.Debugf("RanID: %v for GnbId: %v", ran.RanID(), req.GnbId) 63 | rsp.GnbId = req.GnbId 64 | 65 | // send nf(gnb) status notification 66 | gnbStatus := mi.MetricEvent{ 67 | EventType: mi.CNfStatusEvt, 68 | NfStatusData: mi.CNfStatus{ 69 | NfType: mi.NfTypeGnb, 70 | NfStatus: mi.NfStatusConnected, NfName: req.GnbId, 71 | }, 72 | } 73 | 74 | if *factory.AmfConfig.Configuration.KafkaInfo.EnableKafka { 75 | if err := metrics.StatWriter.PublishNfStatusEvent(gnbStatus); err != nil { 76 | logger.GrpcLog.Errorf("error publishing NfStatusEvent: %v", err) 77 | } 78 | } 79 | } 80 | } 81 | ran.Amf2RanMsgChan = Amf2RanMsgChan 82 | if err := srv.Send(rsp); err != nil { 83 | logger.GrpcLog.Errorln("error in sending response") 84 | } 85 | case sdcoreAmfServer.MsgType_GNB_DISC: 86 | logger.GrpcLog.Infoln("gNB disconnected") 87 | ngap.HandleSCTPNotificationLb(req.GnbId) 88 | // send nf(gnb) status notification 89 | gnbStatus := mi.MetricEvent{ 90 | EventType: mi.CNfStatusEvt, 91 | NfStatusData: mi.CNfStatus{ 92 | NfType: mi.NfTypeGnb, 93 | NfStatus: mi.NfStatusDisconnected, NfName: req.GnbId, 94 | }, 95 | } 96 | if *factory.AmfConfig.Configuration.KafkaInfo.EnableKafka { 97 | if err := metrics.StatWriter.PublishNfStatusEvent(gnbStatus); err != nil { 98 | logger.GrpcLog.Errorf("error publishing NfStatusEvent: %v", err) 99 | } 100 | } 101 | case sdcoreAmfServer.MsgType_GNB_CONN: 102 | logger.GrpcLog.Infoln("new gNB Connected") 103 | // send nf(gnb) status notification 104 | gnbStatus := mi.MetricEvent{ 105 | EventType: mi.CNfStatusEvt, 106 | NfStatusData: mi.CNfStatus{ 107 | NfType: mi.NfTypeGnb, 108 | NfStatus: mi.NfStatusConnected, NfName: req.GnbId, 109 | }, 110 | } 111 | if *factory.AmfConfig.Configuration.KafkaInfo.EnableKafka { 112 | if err := metrics.StatWriter.PublishNfStatusEvent(gnbStatus); err != nil { 113 | logger.GrpcLog.Errorf("error publishing NfStatusEvent: %v", err) 114 | } 115 | } 116 | default: 117 | ngap.DispatchLb(req, Amf2RanMsgChan) 118 | } 119 | } 120 | } 121 | return nil 122 | } 123 | 124 | func StartGrpcServer(port int) { 125 | endpt := fmt.Sprintf(":%d", port) 126 | fmt.Println("listen - ", endpt) 127 | lis, err := net.Listen("tcp", endpt) 128 | if err != nil { 129 | logger.GrpcLog.Errorf("failed to listen: %v", err) 130 | } 131 | 132 | s := Server{} 133 | 134 | grpcServer := grpc.NewServer() 135 | 136 | sdcoreAmfServer.RegisterNgapServiceServer(grpcServer, &s) 137 | 138 | if err := grpcServer.Serve(lis); err != nil { 139 | logger.GrpcLog.Errorf("failed to serve: %v", err) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /util/convert.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 2 | // Copyright 2019 free5GC.org 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package util 8 | 9 | import ( 10 | "encoding/hex" 11 | "fmt" 12 | "strconv" 13 | 14 | "github.com/omec-project/amf/logger" 15 | "github.com/omec-project/nas/nasMessage" 16 | "github.com/omec-project/openapi/models" 17 | ) 18 | 19 | func SnssaiHexToModels(hexString string) (*models.Snssai, error) { 20 | sst, err := strconv.ParseInt(hexString[:2], 16, 32) 21 | if err != nil { 22 | return nil, err 23 | } 24 | sNssai := models.Snssai{ 25 | Sst: int32(sst), 26 | Sd: hexString[2:], 27 | } 28 | return &sNssai, nil 29 | } 30 | 31 | func SnssaiModelsToHex(snssai models.Snssai) string { 32 | sst := fmt.Sprintf("%02x", snssai.Sst) 33 | return sst + snssai.Sd 34 | } 35 | 36 | func SeperateAmfId(amfid string) (regionId, setId, ptrId string, err error) { 37 | if len(amfid) != 6 { 38 | err = fmt.Errorf("len of amfId[%s] != 6", amfid) 39 | return 40 | } 41 | // regionId: 16bits, setId: 10bits, ptrId: 6bits 42 | regionId = amfid[:2] 43 | byteArray, err1 := hex.DecodeString(amfid[2:]) 44 | if err1 != nil { 45 | err = err1 46 | return 47 | } 48 | byteSetId := []byte{byteArray[0] >> 6, byteArray[0]<<2 | byteArray[1]>>6} 49 | setId = hex.EncodeToString(byteSetId)[1:] 50 | bytePtrId := []byte{byteArray[1] & 0x3f} 51 | ptrId = hex.EncodeToString(bytePtrId) 52 | return 53 | } 54 | 55 | func PlmnIdStringToModels(plmnId string) (plmnID models.PlmnId) { 56 | plmnID.Mcc = plmnId[:3] 57 | plmnID.Mnc = plmnId[3:] 58 | return 59 | } 60 | 61 | func TACConfigToModels(intString string) (hexString string) { 62 | tmp, err := strconv.ParseUint(intString, 10, 32) 63 | if err != nil { 64 | logger.UtilLog.Errorf("ParseUint error: %+v", err) 65 | return 66 | } 67 | hexString = fmt.Sprintf("%06x", tmp) 68 | return 69 | } 70 | 71 | func AnTypeToNas(anType models.AccessType) uint8 { 72 | switch anType { 73 | case models.AccessType__3_GPP_ACCESS: 74 | return nasMessage.AccessType3GPP 75 | case models.AccessType_NON_3_GPP_ACCESS: 76 | return nasMessage.AccessTypeNon3GPP 77 | } 78 | 79 | return nasMessage.AccessTypeBoth 80 | } 81 | -------------------------------------------------------------------------------- /util/init_context.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Intel Corporation 2 | // SPDX-FileCopyrightText: 2021 Open Networking Foundation 3 | // Copyright 2019 free5GC.org 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | // 7 | 8 | package util 9 | 10 | import ( 11 | "os" 12 | "time" 13 | 14 | "github.com/google/uuid" 15 | "github.com/omec-project/amf/context" 16 | "github.com/omec-project/amf/factory" 17 | "github.com/omec-project/amf/logger" 18 | "github.com/omec-project/nas/security" 19 | "github.com/omec-project/openapi/models" 20 | "github.com/omec-project/util/drsm" 21 | ) 22 | 23 | func InitDrsm() (drsm.DrsmInterface, error) { 24 | podname := os.Getenv("HOSTNAME") 25 | podip := os.Getenv("POD_IP") 26 | logger.UtilLog.Infof("NfId Instance: %v", context.AMF_Self().NfId) 27 | podId := drsm.PodId{PodName: podname, PodInstance: context.AMF_Self().NfId, PodIp: podip} 28 | logger.UtilLog.Debugf("PodId: %v", podId) 29 | dbUrl := "mongodb://mongodb-arbiter-headless" 30 | if factory.AmfConfig.Configuration.Mongodb != nil && 31 | factory.AmfConfig.Configuration.Mongodb.Url != "" { 32 | dbUrl = factory.AmfConfig.Configuration.Mongodb.Url 33 | } 34 | opt := &drsm.Options{ResIdSize: 24, Mode: drsm.ResourceClient} 35 | db := drsm.DbInfo{Url: dbUrl, Name: factory.AmfConfig.Configuration.AmfDBName} 36 | 37 | // amfid is being used for amfngapid, subscriberid and tmsi for this release 38 | return drsm.InitDRSM("amfid", podId, db, opt) 39 | } 40 | 41 | func InitAmfContext(context *context.AMFContext) { 42 | config := factory.AmfConfig 43 | logger.UtilLog.Infof("amfconfig Info: Version[%s] Description[%s]", config.Info.Version, config.Info.Description) 44 | configuration := config.Configuration 45 | if context.NfId == "" { 46 | context.NfId = uuid.New().String() 47 | } 48 | 49 | if configuration.AmfName != "" { 50 | context.Name = configuration.AmfName 51 | } 52 | if configuration.NgapIpList != nil { 53 | context.NgapIpList = configuration.NgapIpList 54 | } else { 55 | context.NgapIpList = []string{"127.0.0.1"} // default localhost 56 | } 57 | context.NgapPort = configuration.NgapPort 58 | context.SctpGrpcPort = configuration.SctpGrpcPort 59 | sbi := configuration.Sbi 60 | if sbi.Scheme != "" { 61 | context.UriScheme = models.UriScheme(sbi.Scheme) 62 | } else { 63 | logger.UtilLog.Warnln("SBI scheme has not been set. Using http as default") 64 | context.UriScheme = "http" 65 | } 66 | context.RegisterIPv4 = factory.AMF_DEFAULT_IPV4 // default localhost 67 | context.SBIPort = factory.AMF_DEFAULT_PORT_INT // default port 68 | if sbi != nil { 69 | if sbi.RegisterIPv4 != "" { 70 | context.RegisterIPv4 = os.Getenv("POD_IP") 71 | } 72 | if sbi.Port != 0 { 73 | context.SBIPort = sbi.Port 74 | } 75 | if tls := sbi.TLS; tls != nil { 76 | if tls.Key != "" { 77 | context.Key = tls.Key 78 | } 79 | if tls.PEM != "" { 80 | context.PEM = tls.PEM 81 | } 82 | } 83 | context.BindingIPv4 = os.Getenv(sbi.BindingIPv4) 84 | if context.BindingIPv4 != "" { 85 | logger.UtilLog.Infoln("parsing ServerIPv4 address from ENV Variable") 86 | } else { 87 | context.BindingIPv4 = sbi.BindingIPv4 88 | if context.BindingIPv4 == "" { 89 | logger.UtilLog.Warnln("error parsing ServerIPv4 address from string. Using the 0.0.0.0 as default") 90 | context.BindingIPv4 = "0.0.0.0" 91 | } 92 | } 93 | } 94 | serviceNameList := configuration.ServiceNameList 95 | context.InitNFService(serviceNameList, config.Info.Version) 96 | context.ServedGuamiList = configuration.ServedGumaiList 97 | context.SupportTaiLists = configuration.SupportTAIList 98 | // Tac value not converting into 3bytes hex string. 99 | // keeping tac integer value in string format received from configuration 100 | /*for i := range context.SupportTaiLists { 101 | if str := TACConfigToModels(context.SupportTaiLists[i].Tac); str != "" { 102 | context.SupportTaiLists[i].Tac = str 103 | } 104 | }*/ 105 | context.PlmnSupportList = configuration.PlmnSupportList 106 | context.SupportDnnLists = configuration.SupportDnnList 107 | if configuration.NrfUri != "" { 108 | context.NrfUri = configuration.NrfUri 109 | } else { 110 | logger.UtilLog.Warnln("NRF Uri is empty! Using localhost as NRF IPv4 address") 111 | context.NrfUri = factory.AMF_DEFAULT_NRFURI 112 | } 113 | security := configuration.Security 114 | if security != nil { 115 | context.SecurityAlgorithm.IntegrityOrder = getIntAlgOrder(security.IntegrityOrder) 116 | context.SecurityAlgorithm.CipheringOrder = getEncAlgOrder(security.CipheringOrder) 117 | } 118 | context.NetworkName = configuration.NetworkName 119 | context.T3502Value = configuration.T3502Value 120 | context.T3512Value = configuration.T3512Value 121 | context.Non3gppDeregistrationTimerValue = configuration.Non3gppDeregistrationTimerValue 122 | context.T3513Cfg = configuration.T3513 123 | context.T3522Cfg = configuration.T3522 124 | context.T3550Cfg = configuration.T3550 125 | context.T3560Cfg = configuration.T3560 126 | context.T3565Cfg = configuration.T3565 127 | context.EnableSctpLb = configuration.EnableSctpLb 128 | context.EnableDbStore = configuration.EnableDbStore 129 | context.EnableNrfCaching = configuration.EnableNrfCaching 130 | if configuration.EnableNrfCaching { 131 | if configuration.NrfCacheEvictionInterval == 0 { 132 | context.NrfCacheEvictionInterval = time.Duration(900) // 15 mins 133 | } else { 134 | context.NrfCacheEvictionInterval = time.Duration(configuration.NrfCacheEvictionInterval) 135 | } 136 | } 137 | } 138 | 139 | func getIntAlgOrder(integrityOrder []string) (intOrder []uint8) { 140 | for _, intAlg := range integrityOrder { 141 | switch intAlg { 142 | case "NIA0": 143 | intOrder = append(intOrder, security.AlgIntegrity128NIA0) 144 | case "NIA1": 145 | intOrder = append(intOrder, security.AlgIntegrity128NIA1) 146 | case "NIA2": 147 | intOrder = append(intOrder, security.AlgIntegrity128NIA2) 148 | case "NIA3": 149 | intOrder = append(intOrder, security.AlgIntegrity128NIA3) 150 | default: 151 | logger.UtilLog.Errorf("unsupported algorithm: %s", intAlg) 152 | } 153 | } 154 | return 155 | } 156 | 157 | func getEncAlgOrder(cipheringOrder []string) (encOrder []uint8) { 158 | for _, encAlg := range cipheringOrder { 159 | switch encAlg { 160 | case "NEA0": 161 | encOrder = append(encOrder, security.AlgCiphering128NEA0) 162 | case "NEA1": 163 | encOrder = append(encOrder, security.AlgCiphering128NEA1) 164 | case "NEA2": 165 | encOrder = append(encOrder, security.AlgCiphering128NEA2) 166 | case "NEA3": 167 | encOrder = append(encOrder, security.AlgCiphering128NEA3) 168 | default: 169 | logger.UtilLog.Errorf("unsupported algorithm: %s", encAlg) 170 | } 171 | } 172 | return 173 | } 174 | -------------------------------------------------------------------------------- /util/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package util 7 | 8 | import ( 9 | "encoding/json" 10 | "reflect" 11 | 12 | "github.com/omec-project/amf/logger" 13 | ) 14 | 15 | func MarshToJsonString(v interface{}) (result []string) { 16 | types := reflect.TypeOf(v) 17 | val := reflect.ValueOf(v) 18 | if types.Kind() == reflect.Slice { 19 | for i := 0; i < val.Len(); i++ { 20 | tmp, err := json.Marshal(val.Index(i).Interface()) 21 | if err != nil { 22 | logger.UtilLog.Errorf("Marshal error: %+v", err) 23 | } 24 | 25 | result = append(result, string(tmp)) 26 | } 27 | } else { 28 | tmp, err := json.Marshal(v) 29 | if err != nil { 30 | logger.UtilLog.Errorf("Marshal error: %+v", err) 31 | } 32 | 33 | result = append(result, string(tmp)) 34 | } 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /util/mock.drsm.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Open Networking Foundation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package util 7 | 8 | import ( 9 | "github.com/omec-project/amf/logger" 10 | "github.com/omec-project/util/drsm" 11 | ) 12 | 13 | type MockDrsmInterface interface { 14 | AllocateInt32ID() (int32, error) 15 | ReleaseInt32ID(id int32) error 16 | FindOwnerInt32ID(id int32) (*drsm.PodId, error) 17 | AcquireIp(pool string) (string, error) 18 | ReleaseIp(pool, ip string) error 19 | CreateIpPool(poolName string, ipPool string) error 20 | DeleteIpPool(poolName string) error 21 | DeletePod(string) 22 | } 23 | type MockDrsm struct{} 24 | 25 | func MockDrsmInit() (drsm.DrsmInterface, error) { 26 | // db := drsm.DbInfo{"mongodb://mongodb", "amf"} 27 | // podId := drsm.PodId{"amf-instance1", "1.1.1.1"} 28 | // opt := &drsm.Options{ResIdSize: 24, Mode: drsm.ResourceClient} 29 | d := &MockDrsm{} 30 | return d, nil 31 | } 32 | 33 | func (d *MockDrsm) DeletePod(s string) { 34 | logger.AppLog.Info("MockDeletePod") 35 | } 36 | 37 | func (d *MockDrsm) AllocateInt32ID() (int32, error) { 38 | logger.AppLog.Info("MockAllocate") 39 | return 1, nil 40 | } 41 | 42 | func (d *MockDrsm) ReleaseInt32ID(id int32) error { 43 | logger.AppLog.Info("MockRelease") 44 | return nil 45 | } 46 | 47 | func (d *MockDrsm) FindOwnerInt32ID(id int32) (*drsm.PodId, error) { 48 | return nil, nil 49 | } 50 | 51 | func (d *MockDrsm) AcquireIp(pool string) (string, error) { 52 | return "", nil 53 | } 54 | 55 | func (d *MockDrsm) ReleaseIp(pool, ip string) error { 56 | return nil 57 | } 58 | 59 | func (d *MockDrsm) CreateIpPool(poolName string, ipPool string) error { 60 | return nil 61 | } 62 | 63 | func (d *MockDrsm) DeleteIpPool(poolName string) error { 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /util/search_nf_service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 free5GC.org 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package util 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/omec-project/openapi/models" 12 | ) 13 | 14 | func SearchNFServiceUri(nfProfile models.NfProfile, serviceName models.ServiceName, 15 | nfServiceStatus models.NfServiceStatus, 16 | ) (nfUri string) { 17 | if nfProfile.NfServices != nil { 18 | for _, service := range *nfProfile.NfServices { 19 | if service.ServiceName == serviceName && service.NfServiceStatus == nfServiceStatus { 20 | if nfProfile.Fqdn != "" { 21 | nfUri = nfProfile.Fqdn 22 | } else if service.Fqdn != "" { 23 | nfUri = service.Fqdn 24 | } else if service.ApiPrefix != "" { 25 | nfUri = service.ApiPrefix 26 | } else if service.IpEndPoints != nil { 27 | point := (*service.IpEndPoints)[0] 28 | if point.Ipv4Address != "" { 29 | nfUri = getSbiUri(service.Scheme, point.Ipv4Address, point.Port) 30 | } else if len(nfProfile.Ipv4Addresses) != 0 { 31 | nfUri = getSbiUri(service.Scheme, nfProfile.Ipv4Addresses[0], point.Port) 32 | } 33 | } 34 | } 35 | if nfUri != "" { 36 | break 37 | } 38 | } 39 | } 40 | return 41 | } 42 | 43 | func getSbiUri(scheme models.UriScheme, ipv4Address string, port int32) (uri string) { 44 | if port != 0 { 45 | uri = fmt.Sprintf("%s://%s:%d", scheme, ipv4Address, port) 46 | } else { 47 | switch scheme { 48 | case models.UriScheme_HTTP: 49 | uri = fmt.Sprintf("%s://%s:80", scheme, ipv4Address) 50 | case models.UriScheme_HTTPS: 51 | uri = fmt.Sprintf("%s://%s:443", scheme, ipv4Address) 52 | } 53 | } 54 | return 55 | } 56 | -------------------------------------------------------------------------------- /util/test/testAmfcfg.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 free5GC.org 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | info: 7 | version: 1.0.0 8 | description: AMF initial local configuration 9 | 10 | configuration: 11 | amfName: AMF 12 | ngapIpList: 13 | - 127.0.0.1 14 | sbi: 15 | scheme: https 16 | ipv4Addr: 127.0.0.1 17 | port: 29518 18 | serviceNameList: 19 | - namf-comm 20 | - namf-evts 21 | - namf-mt 22 | - namf-loc 23 | servedGuamiList: 24 | - plmnId: 25 | mcc: 208 26 | mnc: 93 27 | amfId: cafe00 28 | supportTaiList: 29 | - plmnId: 30 | mcc: 208 31 | mnc: 93 32 | tac: 000001 33 | plmnSupportList: 34 | - plmnId: 35 | mcc: 208 36 | mnc: 93 37 | snssaiList: 38 | - sst: 1 39 | sd: 010203 40 | supportDnnList: 41 | - internet 42 | nrfUri: https://localhost:29510 43 | security: 44 | integrityOrder: 45 | - NIA2 46 | cipheringOrder: 47 | - NEA2 48 | networkName: 49 | full: aether 50 | -------------------------------------------------------------------------------- /util/test/testAmfcfg2.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 free5GC.org 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | info: 7 | version: 2.0.0 8 | description: AMF initial local configuration 9 | 10 | configuration: 11 | amfName: Wirelab 12 | ngapIpList: 13 | - 127.0.0.1 14 | - 192.188.2.2 15 | sbi: 16 | scheme: http 17 | ipv4Addr: 192.168.0.1 18 | port: 8888 19 | serviceNameList: 20 | - namf-comm 21 | - namf-evts 22 | servedGuamiList: 23 | - plmnId: 24 | mcc: 208 25 | mnc: 93 26 | amfId: cafe00 27 | - plmnId: 28 | mcc: 466 29 | mnc: 46 30 | amfId: 123456 31 | supportTaiList: 32 | - plmnId: 33 | mcc: 208 34 | mnc: 93 35 | tac: 1 36 | - plmnId: 37 | mcc: 208 38 | mnc: 93 39 | tac: 258 40 | - plmnId: 41 | mcc: 466 42 | mnc: 46 43 | tac: 513 44 | plmnSupportList: 45 | - plmnId: 46 | mcc: 208 47 | mnc: 93 48 | snssaiList: 49 | - sst: 1 50 | sd: 010203 51 | - sst: 2 52 | sd: 112233 53 | - plmnId: 54 | mcc: 466 55 | mnc: 46 56 | snssaiList: 57 | - sst: 2 58 | sd: 445566 59 | supportDnnList: 60 | - internet 61 | - wire.cs.nctu.edu.tw 62 | nrfUri: https://192.168.0.2:29510 63 | security: 64 | integrityOrder: 65 | - NIA2 66 | - NIA1 67 | cipheringOrder: 68 | - NEA2 69 | - NEA3 70 | - EEA2 71 | networkName: 72 | full: HAHAHAHA 73 | --------------------------------------------------------------------------------