Repository: wmnsk/go-gtp Branch: main Commit: ecfdc3370199 Files: 435 Total size: 1.5 MB Directory structure: gitextract_o6tx7vdp/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── go.yml │ └── golangci-lint.yml ├── .gitignore ├── .golangci.yml ├── LICENSE ├── README.md ├── doc.go ├── errors.go ├── examples/ │ ├── gw-tester/ │ │ ├── README.md │ │ ├── enb/ │ │ │ ├── config.go │ │ │ ├── enb.go │ │ │ ├── enb.yml │ │ │ ├── main.go │ │ │ └── metrics.go │ │ ├── mme/ │ │ │ ├── config.go │ │ │ ├── handlers.go │ │ │ ├── main.go │ │ │ ├── metrics.go │ │ │ ├── mme.go │ │ │ └── mme.yml │ │ ├── pgw/ │ │ │ ├── config.go │ │ │ ├── handlers.go │ │ │ ├── main.go │ │ │ ├── metrics.go │ │ │ ├── pgw.go │ │ │ └── pgw.yml │ │ ├── s1mme/ │ │ │ ├── s1mme.pb.go │ │ │ └── s1mme.proto │ │ └── sgw/ │ │ ├── config.go │ │ ├── main.go │ │ ├── metrics.go │ │ ├── s11_handlers.go │ │ ├── s5_handlers.go │ │ ├── sgw.go │ │ └── sgw.yml │ ├── mme/ │ │ ├── main.go │ │ ├── mme.go │ │ └── mock.go │ ├── pgw/ │ │ ├── main.go │ │ └── pgw.go │ ├── sgw/ │ │ ├── main.go │ │ ├── s11.go │ │ └── s5.go │ └── utils/ │ └── mac_local_host_enabler.sh ├── go.mod ├── go.sum ├── gtp.go ├── gtp_fuzz_test.go ├── gtp_test.go ├── gtpv0/ │ ├── README.md │ ├── constants.go │ ├── doc.go │ ├── ie/ │ │ ├── apn.go │ │ ├── cause.go │ │ ├── charging-gateway-address.go │ │ ├── charging-id.go │ │ ├── end-user-address.go │ │ ├── errors.go │ │ ├── flow-label.go │ │ ├── gsn-address.go │ │ ├── ie.go │ │ ├── ie_deprecated.go │ │ ├── ie_fuzz_test.go │ │ ├── ie_test.go │ │ ├── imsi.go │ │ ├── ms-not-reachable-reason.go │ │ ├── msisdn.go │ │ ├── p-tmsi-signature.go │ │ ├── p-tmsi.go │ │ ├── private-extension.go │ │ ├── qos-profile.go │ │ ├── rai.go │ │ ├── recovery.go │ │ ├── reordering-required.go │ │ ├── selection-mode.go │ │ └── tlli.go │ ├── message/ │ │ ├── create-pdp-context-req.go │ │ ├── create-pdp-context-req_deprecated.go │ │ ├── create-pdp-context-req_test.go │ │ ├── create-pdp-context-res.go │ │ ├── create-pdp-context-res_deprecated.go │ │ ├── create-pdp-context-res_test.go │ │ ├── delete-pdp-context-req.go │ │ ├── delete-pdp-context-req_deprecated.go │ │ ├── delete-pdp-context-req_test.go │ │ ├── delete-pdp-context-res.go │ │ ├── delete-pdp-context-res_deprecated.go │ │ ├── delete-pdp-context-res_test.go │ │ ├── echo-req.go │ │ ├── echo-req_deprecated.go │ │ ├── echo-req_test.go │ │ ├── echo-res.go │ │ ├── echo-res_deprecated.go │ │ ├── echo-res_test.go │ │ ├── errors.go │ │ ├── generic.go │ │ ├── generic_deprecated.go │ │ ├── generic_test.go │ │ ├── header.go │ │ ├── header_deprecated.go │ │ ├── header_test.go │ │ ├── message.go │ │ ├── message_deprecated.go │ │ ├── message_fuzz_test.go │ │ ├── t-pdu.go │ │ ├── t-pdu_deprecated.go │ │ ├── t-pdu_test.go │ │ ├── update-pdp-context-req.go │ │ ├── update-pdp-context-req_deprecated.go │ │ ├── update-pdp-context-req_test.go │ │ ├── update-pdp-context-res.go │ │ ├── update-pdp-context-res_deprecated.go │ │ └── update-pdp-context-res_test.go │ └── testutils/ │ └── testutils.go ├── gtpv1/ │ ├── README.md │ ├── conn.go │ ├── constants.go │ ├── doc.go │ ├── errors.go │ ├── gtpv1_fuzz_test.go │ ├── handlers.go │ ├── ie/ │ │ ├── apn-restriction.go │ │ ├── apn.go │ │ ├── authentication-quintuplet.go │ │ ├── authentication-triplet.go │ │ ├── cause.go │ │ ├── charging-id.go │ │ ├── common-flags.go │ │ ├── end-user-address.go │ │ ├── errors.go │ │ ├── extended-common-flags-ii.go │ │ ├── extended-common-flags.go │ │ ├── extension-header-type-list.go │ │ ├── gsn-address.go │ │ ├── ie.go │ │ ├── ie_deprecated.go │ │ ├── ie_fuzz_test.go │ │ ├── ie_test.go │ │ ├── imei.go │ │ ├── imei_test.go │ │ ├── imsi.go │ │ ├── ip.go │ │ ├── lac.go │ │ ├── map-cause.go │ │ ├── mcc-mnc.go │ │ ├── ms-timezone.go │ │ ├── ms-validated.go │ │ ├── msisdn.go │ │ ├── nsapi.go │ │ ├── p-tmsi-signature.go │ │ ├── p-tmsi.go │ │ ├── pco.go │ │ ├── private-extension.go │ │ ├── qos-profile.go │ │ ├── rac.go │ │ ├── rai.go │ │ ├── rai_test.go │ │ ├── ranap-cause.go │ │ ├── rat-type.go │ │ ├── recovery.go │ │ ├── reordering-required.go │ │ ├── selection-mode.go │ │ ├── teardown-ind.go │ │ ├── teid.go │ │ ├── uli-timestamp.go │ │ ├── uli.go │ │ └── uli_test.go │ ├── logger.go │ ├── message/ │ │ ├── create-pdp-context-req.go │ │ ├── create-pdp-context-req_deprecated.go │ │ ├── create-pdp-context-req_test.go │ │ ├── create-pdp-context-res.go │ │ ├── create-pdp-context-res_deprecated.go │ │ ├── create-pdp-context-res_test.go │ │ ├── delete-pdp-context-req.go │ │ ├── delete-pdp-context-req_deprecated.go │ │ ├── delete-pdp-context-req_test.go │ │ ├── delete-pdp-context-res.go │ │ ├── delete-pdp-context-res_deprecated.go │ │ ├── delete-pdp-context-res_test.go │ │ ├── echo-req.go │ │ ├── echo-req_deprecated.go │ │ ├── echo-req_test.go │ │ ├── echo-res.go │ │ ├── echo-res_deprecated.go │ │ ├── echo-res_test.go │ │ ├── end-marker.go │ │ ├── end-marker_test.go │ │ ├── error-indication.go │ │ ├── error-indication_deprecated.go │ │ ├── error-indication_test.go │ │ ├── errors.go │ │ ├── extension-header.go │ │ ├── generic.go │ │ ├── generic_deprecated.go │ │ ├── generic_test.go │ │ ├── header.go │ │ ├── header_deprecated.go │ │ ├── header_test.go │ │ ├── message.go │ │ ├── message_deprecated.go │ │ ├── message_fuzz_test.go │ │ ├── supported-extension-header-notification.go │ │ ├── supported-extension-header-notification_test.go │ │ ├── t-pdu.go │ │ ├── t-pdu_deprecated.go │ │ ├── t-pdu_test.go │ │ ├── update-pdp-context-req.go │ │ ├── update-pdp-context-req_deprecated.go │ │ ├── update-pdp-context-req_test.go │ │ ├── update-pdp-context-res.go │ │ ├── update-pdp-context-res_deprecated.go │ │ ├── update-pdp-context-res_test.go │ │ ├── version-not-supported.go │ │ ├── version-not-supported_deprecated.go │ │ └── version-not-supported_test.go │ ├── relay.go │ ├── relay_test.go │ ├── testutils/ │ │ └── testutils.go │ ├── tunnel.go │ ├── tunnel_linux.go │ ├── u-conn.go │ ├── u-conn_test.go │ └── utils.go ├── gtpv2/ │ ├── README.md │ ├── bearer.go │ ├── conn.go │ ├── conn_test.go │ ├── constants.go │ ├── doc.go │ ├── errors.go │ ├── handlers.go │ ├── helpers_test.go │ ├── ie/ │ │ ├── ambr.go │ │ ├── apn-restriction.go │ │ ├── apn.go │ │ ├── arp.go │ │ ├── bearer-context.go │ │ ├── bearer-flags.go │ │ ├── bearer-qos.go │ │ ├── bearer-tft.go │ │ ├── cause.go │ │ ├── charging-characteristics.go │ │ ├── charging-id.go │ │ ├── cmi.go │ │ ├── csg-id.go │ │ ├── delay-value.go │ │ ├── detach-type.go │ │ ├── ebi.go │ │ ├── epc-timer.go │ │ ├── errors.go │ │ ├── f-teid.go │ │ ├── flow-qos.go │ │ ├── fq-csid.go │ │ ├── fqdn.go │ │ ├── global-cn-id.go │ │ ├── guti.go │ │ ├── hop-counter.go │ │ ├── ie.go │ │ ├── ie_deprecated.go │ │ ├── ie_fuzz_test.go │ │ ├── ie_grouped.go │ │ ├── ie_test.go │ │ ├── imsi.go │ │ ├── indication.go │ │ ├── integer-number.go │ │ ├── ip-addr.go │ │ ├── ip-addr_test.go │ │ ├── ldn.go │ │ ├── mbms-flags.go │ │ ├── mcc-mnc.go │ │ ├── mcc-mnc_test.go │ │ ├── mei.go │ │ ├── msisdn.go │ │ ├── node-features.go │ │ ├── node-type.go │ │ ├── p-tmsi-signature.go │ │ ├── p-tmsi.go │ │ ├── paa.go │ │ ├── paging-and-service-information.go │ │ ├── pco-ppp.go │ │ ├── pco-ppp_test.go │ │ ├── pco.go │ │ ├── pdn-type.go │ │ ├── plmn-id.go │ │ ├── port-number.go │ │ ├── private-extension.go │ │ ├── pti.go │ │ ├── ran-nas-cause.go │ │ ├── rat-type.go │ │ ├── recovery.go │ │ ├── rfsp-index.go │ │ ├── s103pdf.go │ │ ├── s1udf.go │ │ ├── selection-mode.go │ │ ├── service-indicator.go │ │ ├── serving-nw.go │ │ ├── tad.go │ │ ├── tft.go │ │ ├── tft_test.go │ │ ├── throttling.go │ │ ├── tmsi.go │ │ ├── trace-reference.go │ │ ├── uci.go │ │ ├── ue-timezone.go │ │ ├── uli-timestamp.go │ │ ├── uli.go │ │ ├── uli_test.go │ │ └── utils.go │ ├── logger.go │ ├── message/ │ │ ├── change-notification-req.go │ │ ├── change-notification-req_test.go │ │ ├── change-notification-res.go │ │ ├── change-notification-res_test.go │ │ ├── context-ack.go │ │ ├── context-ack_deprecated.go │ │ ├── context-ack_test.go │ │ ├── context-req.go │ │ ├── context-req_deprecated.go │ │ ├── context-req_test.go │ │ ├── context-res.go │ │ ├── context-res_deprecated.go │ │ ├── context-res_test.go │ │ ├── create-bearer-req.go │ │ ├── create-bearer-req_deprecated.go │ │ ├── create-bearer-req_test.go │ │ ├── create-bearer-res.go │ │ ├── create-bearer-res_deprecated.go │ │ ├── create-bearer-res_test.go │ │ ├── create-session-req.go │ │ ├── create-session-req_deprecated.go │ │ ├── create-session-req_test.go │ │ ├── create-session-res.go │ │ ├── create-session-res_deprecated.go │ │ ├── create-session-res_test.go │ │ ├── delete-bearer-command.go │ │ ├── delete-bearer-command_test.go │ │ ├── delete-bearer-failure-indication.go │ │ ├── delete-bearer-failure-indication_test.go │ │ ├── delete-bearer-req.go │ │ ├── delete-bearer-req_deprecated.go │ │ ├── delete-bearer-req_test.go │ │ ├── delete-bearer-res.go │ │ ├── delete-bearer-res_deprecated.go │ │ ├── delete-bearer-res_test.go │ │ ├── delete-pdn-connection-set-req.go │ │ ├── delete-pdn-connection-set-req_test.go │ │ ├── delete-pdn-connection-set-res.go │ │ ├── delete-pdn-connection-set-res_test.go │ │ ├── delete-session-req.go │ │ ├── delete-session-req_deprecated.go │ │ ├── delete-session-req_test.go │ │ ├── delete-session-res.go │ │ ├── delete-session-res_deprecated.go │ │ ├── delete-session-res_test.go │ │ ├── detach-acknowledge.go │ │ ├── detach-acknowledge_test.go │ │ ├── detach-notification.go │ │ ├── detach-notification_test.go │ │ ├── downlink-data-notification-ack.go │ │ ├── downlink-data-notification-ack_test.go │ │ ├── downlink-data-notification-failure-indication.go │ │ ├── downlink-data-notification-failure-indication_test.go │ │ ├── downlink-data-notification.go │ │ ├── downlink-data-notification_test.go │ │ ├── echo-req.go │ │ ├── echo-req_deprecated.go │ │ ├── echo-req_test.go │ │ ├── echo-res.go │ │ ├── echo-res_deprecated.go │ │ ├── echo-res_test.go │ │ ├── errors.go │ │ ├── generic.go │ │ ├── generic_deprecated.go │ │ ├── generic_test.go │ │ ├── header.go │ │ ├── header_deprecated.go │ │ ├── header_test.go │ │ ├── message.go │ │ ├── message_deprecated.go │ │ ├── message_fuzz_test.go │ │ ├── message_test.go │ │ ├── modify-access-bearers-req.go │ │ ├── modify-access-bearers-req_deprecated.go │ │ ├── modify-access-bearers-req_test.go │ │ ├── modify-access-bearers-res.go │ │ ├── modify-access-bearers-res_deprecated.go │ │ ├── modify-access-bearers-res_test.go │ │ ├── modify-bearer-command.go │ │ ├── modify-bearer-command_test.go │ │ ├── modify-bearer-failure-indication.go │ │ ├── modify-bearer-failure-indication_test.go │ │ ├── modify-bearer-req.go │ │ ├── modify-bearer-req_deprecated.go │ │ ├── modify-bearer-req_test.go │ │ ├── modify-bearer-res.go │ │ ├── modify-bearer-res_deprecated.go │ │ ├── modify-bearer-res_test.go │ │ ├── pgw-restart-notification-acknowledge.go │ │ ├── pgw-restart-notification-acknowledge_test.go │ │ ├── pgw-restart-notification.go │ │ ├── pgw-restart-notification_test.go │ │ ├── release-access-bearers-req.go │ │ ├── release-access-bearers-req_deprecated.go │ │ ├── release-access-bearers-req_test.go │ │ ├── release-access-bearers-res.go │ │ ├── release-access-bearers-res_deprecated.go │ │ ├── release-access-bearers-res_test.go │ │ ├── resume-acknowledge.go │ │ ├── resume-acknowledge_test.go │ │ ├── resume-notification.go │ │ ├── resume-notification_test.go │ │ ├── stop-paging-indication.go │ │ ├── stop-paging-indication_deprecated.go │ │ ├── stop-paging-indication_test.go │ │ ├── suspend-acknowledge.go │ │ ├── suspend-acknowledge_test.go │ │ ├── suspend-notification.go │ │ ├── suspend-notification_test.go │ │ ├── update-bearer-req.go │ │ ├── update-bearer-req_test.go │ │ ├── update-bearer-res.go │ │ ├── update-bearer-res_test.go │ │ ├── update-pdn-connection-set-req.go │ │ ├── update-pdn-connection-set-req_test.go │ │ ├── update-pdn-connection-set-res.go │ │ ├── update-pdn-connection-set-res_test.go │ │ ├── version-not-supported.go │ │ ├── version-not-supported_deprecated.go │ │ └── version-not-supported_test.go │ ├── session.go │ └── testutils/ │ └── testutils.go └── utils/ ├── utils.go └── utils_test.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: gomod directory: "/" schedule: interval: daily time: "20:00" open-pull-requests-limit: 10 ================================================ FILE: .github/workflows/go.yml ================================================ on: [push, pull_request] name: Test jobs: test-linux: strategy: matrix: go-version: [1.25.x, 1.26.x] runs-on: ubuntu-latest steps: - name: Install Go uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v1 - name: Run unit tests run: go test ./... - name: Run benchmark run: go test -benchmem -bench . ./... - name: Build GW Tester run: | go build -o sgw examples/gw-tester/sgw/*.go go build -o pgw examples/gw-tester/pgw/*.go go build -o mme examples/gw-tester/mme/*.go go build -o enb examples/gw-tester/enb/*.go - name: Run GW Tester run: | sudo ./sgw -config examples/gw-tester/sgw/sgw.yml & sleep 1 sudo ./pgw -config examples/gw-tester/pgw/pgw.yml & sleep 1 sudo ./mme -config examples/gw-tester/mme/mme.yml & sleep 1 sudo ./enb -config examples/gw-tester/enb/enb.yml & sleep 1 - name: Verify GW Tester is working run: | ENB=$(curl -sS http://127.0.10.1:58080/metrics) [ "$(echo "$ENB" | grep "enb_active_sessions [0-9]" | awk '{print $NF}' )" == 5 ] || exit 101 MME=$(curl -sS http://127.0.10.2:58080/metrics) [ "$(echo "$MME" | grep "mme_active_sessions [0-9]" | awk '{print $NF}' )" == 5 ] || exit 101 SGW=$(curl -sS http://127.0.10.3:58080/metrics) [ "$(echo "$SGW" | grep "sgw_active_sessions [0-9]" | awk '{print $NF}' )" == 5 ] || exit 101 PGW=$(curl -sS http://127.0.10.4:58080/metrics) [ "$(echo "$PGW" | grep "pgw_active_sessions [0-9]" | awk '{print $NF}' )" == 5 ] || exit 101 test-macos: strategy: matrix: go-version: [1.25.x, 1.26.x] runs-on: macos-latest steps: - name: Install Go uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v1 - name: Setup loopback interface for tests run: | sudo ifconfig lo0 alias 127.0.0.2 sudo ifconfig lo0 alias 127.0.0.11 sudo ifconfig lo0 alias 127.0.0.12 - name: Test run: go test $(go list ./... | grep -v '/examples') - name: Bench run: go test -benchmem -bench . $(go list ./... | grep -v '/examples') # test-windows: # strategy: # matrix: # go-version: [1.25.x, 1.26.x] # runs-on: windows-latest # steps: # - name: Install Go # uses: actions/setup-go@v5 # with: # go-version: ${{ matrix.go-version }} # - name: Checkout code # uses: actions/checkout@v1 # - name: Test # run: go test ./... # - name: Bench # run: go test -benchmem -bench . ./... ================================================ FILE: .github/workflows/golangci-lint.yml ================================================ name: golangci-lint on: push: tags: - v* branches: - main pull_request: jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true - name: golangci-lint uses: golangci/golangci-lint-action@v9 with: version: latest ================================================ FILE: .gitignore ================================================ examples/mme/mme examples/pgw/pgw examples/sgw/sgw examples/gw-tester/enb/enb examples/gw-tester/pgw/pgw examples/gw-tester/sgw/sgw examples/gw-tester/mme/mme **/testdata/ ================================================ FILE: .golangci.yml ================================================ version: "2" run: issues-exit-code: 1 tests: false linters: default: none enable: - asciicheck - bodyclose - dogsled - errcheck - errorlint - goconst - gocritic - gomodguard - goprintffuncname - gosec - govet - ineffassign - misspell - nakedret - prealloc - rowserrcheck - staticcheck - unconvert - unparam - unused settings: gosec: excludes: - G115 exclusions: generated: lax presets: - comments - common-false-positives - legacy - std-error-handling paths: - third_party$ - builtin$ - examples$ issues: max-issues-per-linter: 4095 max-same-issues: 1023 new: true formatters: enable: - gofmt - goimports exclusions: generated: lax paths: - third_party$ - builtin$ - examples$ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019-2025 Yoshiyuki Kurauchi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # go-gtp: GTP in Go Package gtp provides simple and painless handling of GTP (GPRS Tunneling Protocol) in the Go programming language. ![CI status](https://github.com/wmnsk/go-gtp/actions/workflows/go.yml/badge.svg) [![golangci-lint](https://github.com/wmnsk/go-gtp/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/wmnsk/go-gtp/actions/workflows/golangci-lint.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/wmnsk/go-gtp.svg)](https://pkg.go.dev/github.com/wmnsk/go-gtp) [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/wmnsk/go-gtp/blob/main/LICENSE) ## Project Status This project is still EXPERIMENTAL. Any part of the implementations (including exported APIs) may be changed before released as v1.0.0. ## Features * Flexible enough to control everything in the GTP protocol, making it suitable for developing mobile core network nodes or testing tools for them. * Provides many helpers that are kind to developers, such as session, bearer, and TEID management. * Makes it easy to handle multiple connections with fixed IP and Port with UDP (or other `net.PacketConn`). * Currently works only on Linux and macOS since netlink support is introduced. However, the plan is to make it work on Windows in the future. ## Getting Started ### Prerequisites go-gtp supports Go Modules. Run `go mod tidy` in your project's directory to collect the required packages automatically. ``` go mod tidy ``` _This project follows [the Release Policy of Go](https://golang.org/doc/devel/release.html#policy)._ ### Running examples #### End-to-end We have a set of tools called GW Tester at [`examples/gw-tester`](./examples/gw-tester). See the [document](./examples/gw-tester/README.md) for how it works and how to run it. It is also used for the integration test of this project. [Workflow setting](./.github/workflows/go.yml) may help you understand it as well. #### Individual node [The examples](examples/) work as it is by `go build` and executing commands in the following way. *Note for macOS users*: before running any go service, make sure to execute `./mac_local_host_enabler.sh` you will find at [examples/utils](examples/utils). 1. Open four terminals on a machine and start capturing on the loopback interface. 2. Start P-GW on terminal #1 and #2 ```shell-session // on terminal #1 ./pgw // on terminal #2 ./pgw -s5c 127.0.0.53 -s5u 127.0.0.5 ``` 3. Start S-GW on terminal #3 ```shell-session // on terminal #3 ./sgw ``` 4. Start MME on terminal #4 ```shell-session // on terminal #4 ./mme ``` 5. You will see the nodes exchanging Create Session and Modify Bearer on C-Plane, and ICMP Echo on U-Plane afterwards. _The "mme" is not an MME per se. In addition to S11 interface, it also mocks UEs and an eNB to establish sessions and send packets on U-Plane._ ## Developing with go-gtp This section briefly describes how to develop your own GTP node with go-gtp. For the detailed usage of a specific version, see README.md under each version's directory. | Version | Details | | ------- | ---------------------------- | | GTPv0 | [README.md](gtpv0/README.md) | | GTPv1 | [README.md](gtpv1/README.md) | | GTPv2 | [README.md](gtpv2/README.md) | ### Establishing a connection between nodes Each version has `net.PacketConn`-like APIs and GTP-specific ones, which are often version-specific. The basic idea behind the current implementation is; * `Dial` or `ListenAndServe` to create a connection (`Conn`) between nodes. * Register handlers to the `Conn` for specific messages with `AddHandler`, allowing users to handle the messages coming from the remote endpoint as flexible as possible, with less pain. * `CreateXXX` to create session or PDP context with arbitrary IEs given. Session/PDP context is structured, and they also have some helpers like `AddTEID` to handle known TEID properly. ### Handling messages and IEs #### Messages All the messages implement the same interface: `message.Message`, and have their own structs named ``, which can be created by `New` with given `ie.IE`s. `message.Message` can be sent on top of `Conn` with `SendMessageTo`, or can be serialized into `[]byte` with `Marshal`. To parse the message from `[]byte`, use `message.Parse`. The parsed message will be one of the structs that implement `message.Message`, and you can type-assert it to the corresponding struct to access the fields which are a set of `ie.IE`s. #### IEs All the IEs are of the same type: `ie.IE` (not an interface). An IE can be created either with `New`, with `ie.New`, or with `ie.New`. The latter two are useful when you want to create an IE with an unsupported type or our constructor does not work well for you. To parse the IE from `[]byte`, use `ie.Parse` (note that `message.Parse` parses all the IEs on a message - you don't need to call `ie.Parse` when you're handling IEs on a message). The value of the parsed `ie.IE` can be retrieved with ``, `ValueAs`. Some of the complicated IEs have their own struct named `Fields` to get the values by accessing the fields. For grouped IEs, accesing the `ChildIEs` field and iterating over the list of IEs contained is the most efficient way in most cases. Though there are the methods to get the specific IE value from the list (e.g., `BearerFlags` can be called upon `BearerContext` IE), they are not recommended since they always parse the whole list of IEs again. ## Supported Features Note that "supported" means that the package provides helpers that make it easier to handle. In other words, even if a message/IE is not marked as "Yes", you can make it work with some additional effort. Your contribution is welcome to implement helpers for all the types, of course! | Version | Messages | IEs | Networking (state machine) | Details | | ----------------- | -------- | ---- | ------------------------------------------- | -------------------------------------------------------- | | GTPv0 | ~35% | ~80% | not implemented yet | [gtpv0/README](gtpv0/README.md#supported-features) | | GTPv1 | ~25% | ~30% | v1-U: functional
v1-C: not implemented | [gtpv1/README](gtpv1/README.md#supported-features) | | GTPv2 | ~40.0% | ~45% | functional | [gtpv2/README](gtpv2/README.md#supported-features) | | GTP'
(Prime) | N/A | N/A | N/A | _not planned_ | _You may also be interested in the sibling project [go-pfcp](https://github.com/wmnsk/go-pfcp) which is a PFCP implementation in Go._ ## Contributing With the design goal of being flexible and easy to use, go-gtp is still in the early stage of development. Any contribution is welcome! Please feel free to open an issue or a pull request. Please don't forget to run tests once you are done with your changes. Additionally, running the fuzz test is recommended to make sure that the implementation is robust enough. ```shell-session go test ./... go test -fuzz . ``` *Note for macOS users*: the first time you run any test, make sure to execute `./mac_local_host_enabler.sh` you will find at [examples/utils](examples/utils). You will have to run the script again after each reboot. ## Authors [Yoshiyuki Kurauchi](https://wmnsk.com/) and [contributors](https://github.com/wmnsk/go-gtp/graphs/contributors). ## LICENSE [MIT](https://github.com/wmnsk/go-gtp/blob/main/LICENSE) ================================================ FILE: doc.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Package gtp provides simple and painless handling of GTP (GPRS Tunneling Protocol). // // This package is the wrapper for all versions of GTP. Please see the godocs of each version instead. // // GTPv0: https://pkg.go.dev/github.com/wmnsk/go-gtp/v0 // // GTPv1: https://pkg.go.dev/github.com/wmnsk/go-gtp/v1 // // GTPv2: https://pkg.go.dev/github.com/wmnsk/go-gtp/v2 // // Please also see README.md for detailed usage of the APIs provided by this package, // as well as how to run the examples. // // https://github.com/wmnsk/go-gtp/blob/main/README.md // // https://github.com/wmnsk/go-gtp/tree/main/examples/gw-tester package gtp ================================================ FILE: errors.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtp import "errors" // Common error definitions. var ( ErrInvalidVersion = errors.New("got invalid version") ErrInvalidLength = errors.New("length value is invalid") ErrTooShortToParse = errors.New("too short to decode as GTP") ErrTooShortToMarshal = errors.New("too short to serialize") ) ================================================ FILE: examples/gw-tester/README.md ================================================ # GW Tester A pseudo eNB and MME as a tester for S/P-GW. ## What's this? ![diagram](./docs/diagram.png) It is a burden to use actual UE/eNB/MME just to test S/P-GW, isn't it? GW Tester emulates the minimal required behavior of surrounding nodes to perform quick and simple testing on S/P-GW. A blog post by the author is available [here](https://wmnsk.com/posts/20200116_gw-tester/) for those who are interested in :) _NOTE: Some of the blog post's configurations or codes might be no longer relevant in the current version._ ## How it works ### Authentication Nothing! Subscribers defined in the `enb.yml` file can immediately attach and use the created sessions. MME accepts any subscribers without an authentication procedure. Communication over the S1-MME interface is done with protobuf/gRPC instead of the S1AP protocol. ``` === AD === Looking for a Go package for LTE authentication? MILENAGE algorithm implementation is available :) https://github.com/wmnsk/milenage ``` ### Gateway Selection No DNS lookup by TAI/APN is implemented. MME just chooses gateways according to the mapping of source IP ranges and GW's IPs defined in `mme.yml`. This behavior might be changed in the future. ### Session Establishment MME exchanges the real GTPv2 session establishment messages like Create/Modify/Delete Session with S-GW. * IP address assignment Currently, we use the IP address that is defined in `enb.yml`, and the one passed by P-GW is ignored. This behavior might be changed in the future to be more practical. * TEID allocation It can be both static and dynamic. Random TEID will be allocated by enb if `i_tei` in `enb.yml` is set to `0`. For outgoing TEID, the one that S-GW allocates will be used. ### U-Plane Data Injection eNB forwards incoming traffic from UE or generates traffic by itself depending on the `type` in `enb.yml`. GTP-U feature is based on [Linux Kernel GTP-U](https://www.kernel.org/doc/Documentation/networking/gtp.txt) with netlink. | type | behavior | | -------- | --------------------------------------------------------------------------------------------------------------- | | external | eNB encapsulates and forwards the incoming packets from `src_ip` toward the specified interface(`euu_if_name`). | | http_get | eNB starts sending HTTP GET to the specified URL. | | ... | _(other types might be implemented in the future!)_ | ## Getting Started ### Prerequisites * Linux (kernel >= 4.12) with root privilege * `net.ip_forward` enabled * ports opened: 36412/TCP, 2123/UDP, 2152/UDP ### Run testers Just `go get` eNB and MME. Functional S-GW and P-GW are also available in the same directory if you need them. ```shell-session go get github.com/wmnsk/go-gtp/examples/gw-tester/enb go get github.com/wmnsk/go-gtp/examples/gw-tester/mme ``` And run them with YAML configuration. See [Configuration](#configurations) section for details. ```shell-session ./mme ``` ```shell-session ./enb ``` Then you'll see; * MME starts sending GTPv2 Create Session Request to S-GW after it receives subscriber information from eNB. * When sessions are successfully created on S/P-GW, eNB sets up GTP-U tunnels with S-GW. After the successful creation of the sessions, you can inject packets externally or generate them on eNB. ## Configurations Each node has a YAML file as a configuration. In general, config consists of the network information of local/remote nodes and some node-specific parameters. ### eNB #### Global These values are used to identify eNB. Some of them are just to be set inside the packets and not validated. | config | type of value | description | | ---------------- | ------------- | ----------------------------------------------------------------- | | `mcc` | string | MCC of eNB | | `mnc` | string | MNC of eNB | | `rat_type` | uint8 | RAT Type (`6` for E-UTRAN) | | `tai` | uint16 | TAI of eNB | | `eci` | uint32 | ECI of eNB | | `mme_addr` | string | IP/Port of MME to dial, for S1-MME interface | | `use_kernel_gtp` | bool | Use Kernel GTP or not. U-Plane does not work when set to `false`. | | `prom_addr` | string | IP/Port of MME to serve Prometheus | #### Local Addresses `local_addresses` are the IP addresses/ports to be bound on the local machine. | config | type of value | description | | -------- | ------------- | ----------------------------- | | `s1c_ip` | string | local IP for S1-MME interface | | `s1u_ip` | string | local IP for S1-U interface | #### Subscribers `subscribers` are the list of subscribers to attach. | config | type of value | description | | -------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `imsi` | string | IMSI of the subscriber | | `msisdn` | string | MSISDN of the subscriber | | `imeisv` | string | IMEISV of the subscriber | | `src_ip` | string | source IP of the subscriber (not assigned by P-GW) | | `i_tei` | uint32 | incoming TEID that S-GW should specify the subscriber | | `type` | string | `external` or `http_get`. see [U-Plane Data Injection](#u-plane-data-injection) | | `euu_if_name` | string | name of the network interface on eUu side.
type=`external`: Used to receive traffic from external UE
type=`http_get`: Used as a source interface that `src_ip` is added | | `http_url` | string | URL to HTTP GET by built-in traffic generator | | `reattach_on_reload` | bool | whether to perform the attach procedure again on config reload | ### MME #### Global These values are used to identify MME. Some of them are just to be set inside the packets and not validated. | config | type of value | description | | ----------- | ------------- | ---------------------------------- | | `mcc` | string | MCC of MME | | `mnc` | string | MNC of MME | | `apn` | string | APN assigned to all the subscriber | | `prom_addr` | string | IP/Port of MME to serve Prometheus | #### Local Addresses `local_addresses` are the IP addresses/ports to be bound on the local machine. | config | type of value | description | | ---------- | ------------- | ---------------------------------- | | `s1c_addr` | string | local IP/Port for S1-MME interface | | `s11_ip` | string | local IP for S11 interface | #### Gateway IPs IP addresses required to know/tell S-GW. This is typically done by DNS lookup with APN, but it's static for now. | config | type of value | description | | ------------ | ------------- | ---------------------------- | | `sgw_s11_ip` | string | S-GW's IP for S11 interface | | `pgw_s5c_ip` | string | P-GW's IP for S5-C interface | ### S-GW #### Local Addresses `local_addresses` are the IP addresses/ports to be bound on the local machine. | config | type of value | description | | ---------------- | ------------- | ----------------------------------- | | `s11_ip` | string | local IP for S11 interface | | `s1u_ip` | string | local IP for S1-U interface | | `s5c_ip` | string | local IP for S5-C interface | | `s5u_ip` | string | local IP for S5-U interface | | `use_kernel_gtp` | bool | Use Kernel GTP or not. | | `prom_addr` | string | IP/Port of S-GW to serve Prometheus | ### P-GW #### Global | config | type of value | description | | ---------------- | ------------- | -------------------------------------------------------------------------- | | `sgi_if_name` | string | name of the network interface on SGi side. Used to downlink route traffic. | | `route_subnet` | string | IP subnet of UEs that should be routed properly. | | `use_kernel_gtp` | bool | Use Kernel GTP or not. U-Plane does not work when set to `false`. | | `prom_addr` | string | IP/Port of P-GW to serve Prometheus | #### Local Addresses `local_addresses` are the IP addresses/ports to be bound on the local machine. | config | type of value | description | | -------- | ------------- | --------------------------- | | `s5c_ip` | string | local IP for S5-C interface | | `s5u_ip` | string | local IP for S5-U interface | | `sgi_ip` | string | local IP for SGi interface | ## Other Features ### Reloading config The programs can handle `SIGHUP` to reload config without deleting sessions. Update YAML file and send `SIGHUP` to the process. ### Instrumentation GW Tester nodes expose some metrics for Prometheus if `prom_addr` is given in each config. You can see the sample response from each node in [this Gist](https://gist.github.com/wmnsk/72f6d2d2450452090cd6351ffe63f660). I'm planning to add some more metrics like "success rate of HTTP probe", etc. | Metrics | Name | Description | | ----------------- | ------------------------------------- | --------------------------------------------- | | Active sessions | `_active_sessions` | number of session established currently | | Active bearers | `_active_bearers` | number of GTP-U tunnels established currently | | Messages sent | `_messages_sent_total` | number of messages sent by messagge type | | Messages received | `_messages_received_total` | number of messages received by messagge type | ================================================ FILE: examples/gw-tester/enb/config.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "fmt" "io/ioutil" "gopkg.in/yaml.v2" ) // Config is a configurations loaded from yaml. type Config struct { LocalAddrs struct { S1CIP string `yaml:"s1c_ip"` S1UIP string `yaml:"s1u_ip"` } `yaml:"local_addresses"` MMEAddr string `yaml:"mme_addr"` PromAddr string `yaml:"prom_addr"` MCC string `yaml:"mcc"` MNC string `yaml:"mnc"` RATType uint8 `yaml:"rat_type"` TAI uint16 `yaml:"tai"` ECI uint32 `yaml:"eci"` Subscribers []*Subscriber `yaml:"subscribers"` UseKernelGTP bool `yaml:"use_kernel_gtp"` } // Subscriber represents a subscriber. type Subscriber struct { IMSI string `yaml:"imsi"` MSISDN string `yaml:"msisdn"` IMEISV string `yaml:"imeisv"` SrcIP string `yaml:"src_ip"` ITEI uint32 `yaml:"i_tei"` TrafficType string `yaml:"type"` EUuIFName string `yaml:"euu_if_name"` HTTPURL string `yaml:"http_url"` Reattach bool `yaml:"reattach_on_reload"` // values for these fields are given from MME. sgwAddr string otei uint32 } // String returns the information of s in string. func (s *Subscriber) String() string { return fmt.Sprintf( "IMSI: %s, MSISDN: %s, IMEISV: %s, SrcIP: %s, S-GW: %s, I_TEI: %#08x, O_TEI: %#08x", s.IMSI, s.MSISDN, s.IMEISV, s.SrcIP, s.sgwAddr, s.ITEI, s.otei, ) } func loadConfig(path string) (*Config, error) { buf, err := ioutil.ReadFile(path) if err != nil { return nil, err } c := &Config{} if err := yaml.Unmarshal(buf, c); err != nil { return nil, err } return c, nil } ================================================ FILE: examples/gw-tester/enb/enb.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "context" "crypto/rand" "encoding/binary" "fmt" "log" "net" "net/http" "sync" "time" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/vishvananda/netlink" "google.golang.org/grpc" "github.com/wmnsk/go-gtp/examples/gw-tester/s1mme" "github.com/wmnsk/go-gtp/gtpv1" ) type enb struct { mu sync.Mutex // S1-MME mmeAddr net.Addr cConn *grpc.ClientConn s1mmeClient s1mme.AttacherClient // S1-U uAddr net.Addr uConn *gtpv1.UPlaneConn location *s1mme.Location candidateSubs []*Subscriber sessions []*Subscriber attachCh chan *Subscriber useKernelGTP bool addedAddrs map[netlink.Link]*netlink.Addr addedRoutes []*netlink.Route addedRules []*netlink.Rule promAddr string mc *metricsCollector errCh chan error } func newENB(cfg *Config) (*enb, error) { e := &enb{ mu: sync.Mutex{}, location: &s1mme.Location{ Mcc: cfg.MCC, Mnc: cfg.MNC, RatType: s1mme.Location_RATType(cfg.RATType), Tai: uint32(cfg.TAI), Eci: cfg.ECI, }, candidateSubs: cfg.Subscribers, useKernelGTP: cfg.UseKernelGTP, addedAddrs: make(map[netlink.Link]*netlink.Addr), errCh: make(chan error, 1), } var err error e.uAddr, err = net.ResolveUDPAddr("udp", cfg.LocalAddrs.S1UIP+gtpv1.GTPUPort) if err != nil { return nil, err } e.mmeAddr, err = net.ResolveTCPAddr("tcp", cfg.MMEAddr) if err != nil { return nil, err } if cfg.PromAddr != "" { // validate if the address is valid or not. if _, err = net.ResolveTCPAddr("tcp", cfg.PromAddr); err != nil { return nil, err } e.promAddr = cfg.PromAddr } if !e.useKernelGTP { log.Println("WARN: U-Plane does not work without GTP kernel module") } return e, nil } func (e *enb) run(ctx context.Context) error { // TODO: bind local address(cfg.LocalAddrs.S1CIP) with WithDialer option? conn, err := grpc.Dial(e.mmeAddr.String(), grpc.WithInsecure()) if err != nil { return err } e.s1mmeClient = s1mme.NewAttacherClient(conn) log.Printf("Established S1-MME connection with %s", e.mmeAddr) e.uConn = gtpv1.NewUPlaneConn(e.uAddr) if e.useKernelGTP { if err := e.uConn.EnableKernelGTP("gtp-enb", gtpv1.RoleSGSN); err != nil { return err } } go func() { if err := e.uConn.ListenAndServe(ctx); err != nil { log.Println(err) return } log.Println("uConn.ListenAndServe exitted") }() log.Printf("Started serving S1-U on %s", e.uAddr) // start serving Prometheus, if address is given if e.promAddr != "" { if err := e.runMetricsCollector(); err != nil { return err } http.Handle("/metrics", promhttp.Handler()) go func() { if err := http.ListenAndServe(e.promAddr, nil); err != nil { log.Println(err) } }() log.Printf("Started serving Prometheus on %s", e.promAddr) } for _, sub := range e.candidateSubs { select { case <-ctx.Done(): return nil case <-time.After(10 * time.Millisecond): // not to load too much in case of many subscribers } if err := e.attach(ctx, sub); err != nil { return err } } // wait for new subscribers to be attached e.attachCh = make(chan *Subscriber) defer close(e.attachCh) for { select { case <-ctx.Done(): return nil case sub := <-e.attachCh: go func() { if err := e.attach(ctx, sub); err != nil { log.Printf("Failed to attach: %s: %s", sub, err) } }() // wait 10ms after dispatching time.Sleep(10 * time.Millisecond) } } } func (e *enb) reload(cfg *Config) error { e.location = &s1mme.Location{ Mcc: cfg.MCC, Mnc: cfg.MNC, RatType: s1mme.Location_RATType(cfg.RATType), Tai: uint32(cfg.TAI), Eci: cfg.ECI, } // TODO: consider more efficient way var attachSubs []*Subscriber for _, sub := range cfg.Subscribers { var existing bool for _, attached := range e.sessions { if sub.IMSI == attached.IMSI { existing = true } } if existing { if !sub.Reattach { continue } if err := e.uConn.DelTunnelByITEI(sub.ITEI); err != nil { continue } } attachSubs = append(attachSubs, sub) } for _, sub := range attachSubs { e.attachCh <- sub } return nil } func (e *enb) close() error { var errs []error e.mu.Lock() defer e.mu.Unlock() for l, a := range e.addedAddrs { if err := netlink.AddrDel(l, a); err != nil { errs = append(errs, err) } } for _, r := range e.addedRoutes { if err := netlink.RouteDel(r); err != nil { errs = append(errs, err) } } for _, r := range e.addedRules { if err := netlink.RuleDel(r); err != nil { errs = append(errs, err) } } if c := e.uConn; c != nil { if err := c.Close(); err != nil { errs = append(errs, err) } } if c := e.cConn; c != nil { if err := c.Close(); err != nil { errs = append(errs, err) } } return fmt.Errorf("errors while closing eNB: %v", errs) } func (e *enb) attach(ctx context.Context, sub *Subscriber) error { // allocate random TEID if 0 is specified in config. if sub.ITEI == 0 { sub.ITEI = e.newTEID() } req := &s1mme.AttachRequest{ Imsi: sub.IMSI, Msisdn: sub.MSISDN, Imeisv: sub.IMEISV, S1UAddr: e.uConn.LocalAddr().String(), SrcIp: sub.SrcIP, ITei: sub.ITEI, Location: e.location, } rsp, err := e.s1mmeClient.Attach(ctx, req) if err != nil { return err } if e.mc != nil { e.mc.messagesSent.WithLabelValues(e.mmeAddr.String(), "Attach Request").Inc() e.mc.messagesReceived.WithLabelValues(e.mmeAddr.String(), "Attach Response").Inc() } switch rsp.Cause { case s1mme.Cause_SUCCESS: sgwIP, _, err := net.SplitHostPort(rsp.SgwAddr) if err != nil { return err } if e.useKernelGTP { if err := e.uConn.AddTunnelOverride( net.ParseIP(sgwIP), net.ParseIP(req.SrcIp), rsp.OTei, req.ITei, ); err != nil { log.Println(net.ParseIP(sgwIP), net.ParseIP(req.SrcIp), rsp.OTei, req.ITei) e.errCh <- fmt.Errorf("failed to create tunnel for %s: %w", sub.IMSI, err) return nil } if err := e.addRoute(); err != nil { e.errCh <- fmt.Errorf("failed to add route for %s: %w", sub.IMSI, err) return nil } } sub.sgwAddr = rsp.SgwAddr sub.otei = rsp.OTei e.sessions = append(e.sessions, sub) if e.useKernelGTP { if err := e.setupUPlane(ctx, sub); err != nil { e.errCh <- fmt.Errorf("failed to setup U-Plane for %s: %w", sub.IMSI, err) return nil } log.Printf("Successfully established tunnel for %s", sub.IMSI) } default: e.errCh <- fmt.Errorf("got unexpected Cause for %s: %s", rsp.Cause, sub.IMSI) return nil } return nil } func (e *enb) newTEID() uint32 { b := make([]byte, 4) if _, err := rand.Read(b); err != nil { return 0 } generated := binary.BigEndian.Uint32(b) for _, s := range e.sessions { if generated == s.ITEI { return e.newTEID() } } return generated } func (e *enb) setupUPlane(ctx context.Context, sub *Subscriber) error { switch sub.TrafficType { case "http_get": if err := e.addIP(sub); err != nil { return err } if err := e.addRuleLocal(sub); err != nil { return err } go func() { if err := e.runHTTPProbe(ctx, sub); err != nil { e.errCh <- err } }() case "external": if err := e.addRuleExternal(sub); err != nil { return err } default: return fmt.Errorf("unknown/unimplemented type: %s specified for 'type' in subscriber", sub.TrafficType) } return nil } func (e *enb) addRoute() error { route := &netlink.Route{ Dst: &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, // default LinkIndex: e.uConn.KernelGTP.Link.Attrs().Index, // dev gtp- Scope: netlink.SCOPE_LINK, // scope link Protocol: 4, // proto static Priority: 1, // metric 1 Table: 1001, // table } e.addedRoutes = append(e.addedRoutes, route) return netlink.RouteReplace(route) } func (e *enb) addRuleExternal(sub *Subscriber) error { rules, err := netlink.RuleList(0) if err != nil { return err } mask32 := &net.IPNet{IP: net.ParseIP(sub.SrcIP), Mask: net.CIDRMask(32, 32)} for _, r := range rules { if r.IifName == sub.EUuIFName && r.Src == mask32 && r.Table == 1001 { return nil } } rule := netlink.NewRule() rule.IifName = sub.EUuIFName rule.Src = mask32 rule.Table = 1001 e.addedRules = append(e.addedRules, rule) return netlink.RuleAdd(rule) } func (e *enb) addRuleLocal(sub *Subscriber) error { rules, err := netlink.RuleList(0) if err != nil { return err } mask32 := &net.IPNet{IP: net.ParseIP(sub.SrcIP), Mask: net.CIDRMask(32, 32)} for _, r := range rules { if r.Src == mask32 && r.Table == 1001 { return nil } } rule := netlink.NewRule() rule.Src = mask32 rule.Table = 1001 e.addedRules = append(e.addedRules, rule) return netlink.RuleAdd(rule) } func (e *enb) runHTTPProbe(ctx context.Context, sub *Subscriber) error { laddr, err := net.ResolveTCPAddr("tcp", sub.SrcIP+":0") if err != nil { return err } dialer := net.Dialer{LocalAddr: laddr} client := http.Client{ Transport: &http.Transport{Dial: dialer.Dial}, Timeout: 3 * time.Second, } for { select { case <-ctx.Done(): return nil case <-time.After(5 * time.Second): // do nothing here and go forward } rsp, err := client.Get(sub.HTTPURL) if err != nil { e.errCh <- fmt.Errorf("failed to GET %s: %w", sub.HTTPURL, err) continue } if rsp.StatusCode == http.StatusOK { log.Printf("[HTTP Probe;%s] Successfully GET %s: Status: %s", sub.IMSI, sub.HTTPURL, rsp.Status) rsp.Body.Close() continue } rsp.Body.Close() e.errCh <- fmt.Errorf("got invalid response on HTTP probe: %v", rsp.StatusCode) } } func (e *enb) addIP(sub *Subscriber) error { link, err := netlink.LinkByName(sub.EUuIFName) if err != nil { return err } addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL) if err != nil { return err } netToAdd := &net.IPNet{IP: net.ParseIP(sub.SrcIP), Mask: net.CIDRMask(24, 32)} var addr netlink.Addr var found bool for _, a := range addrs { if a.Label == sub.EUuIFName { if a.IPNet.String() == netToAdd.String() { return nil } addr = a found = true } } if !found { return fmt.Errorf("cannot find the interface to add address: %s", sub.EUuIFName) } addr.IPNet = netToAdd if err := netlink.AddrAdd(link, &addr); err != nil { return err } e.mu.Lock() defer e.mu.Unlock() e.addedAddrs[link] = &addr return nil } ================================================ FILE: examples/gw-tester/enb/enb.yml ================================================ mcc: "001" mnc: "01" rat_type: 6 # E-UTRAN tai: 0x0001 eci: 0x00000001 local_addresses: s1c_ip: "127.0.1.11" s1u_ip: "127.0.0.11" mme_addr: "127.0.1.12:36412" use_kernel_gtp: false prom_addr: "127.0.10.1:58080" subscribers: - imsi: "001010000000001" msisdn: "814000000001" imeisv: "1234500000001" src_ip: "192.168.101.201" i_tei: 1 type: "http_get" euu_if_name: "lo" http_url: "http://172.16.0.102/" reattach_on_reload: false - imsi: "001010000000002" msisdn: "814000000002" imeisv: "1234500000002" src_ip: "192.168.101.202" i_tei: 0 type: "http_get" euu_if_name: "lo" http_url: "http://172.16.0.102/" reattach_on_reload: true - imsi: "001010000000003" msisdn: "814000000003" imeisv: "1234500000003" src_ip: "192.168.101.203" i_tei: 0 type: "http_get" euu_if_name: "lo" http_url: "http://172.16.0.102/" reattach_on_reload: false - imsi: "001010000000004" msisdn: "814000000004" imeisv: "1234500000004" src_ip: "192.168.101.204" i_tei: 0 type: "http_get" euu_if_name: "lo" http_url: "http://172.16.0.102/" reattach_on_reload: true - imsi: "001010000000005" msisdn: "814000000005" imeisv: "1234500000005" src_ip: "192.168.101.205" i_tei: 0 type: "http_get" euu_if_name: "lo" http_url: "http://172.16.0.102/" reattach_on_reload: false # example for external client #- imsi: "001010000000006" # msisdn: "814000000006" # imeisv: "1234500000006" # src_ip: "192.168.101.101" # i_tei: 1 # type: "external" # euu_if_name: "eth0" # http_url: "" # reattach_on_reload: true ================================================ FILE: examples/gw-tester/enb/main.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Command enb works as pseudo eNB that forwards packets through GTPv1 tunnel. package main import ( "context" "flag" "log" "os" "os/signal" "syscall" ) func main() { var configPath = flag.String("config", "./enb.yml", "Path to the configuration file.") flag.Parse() log.SetPrefix("[eNB] ") cfg, err := loadConfig(*configPath) if err != nil { log.Fatal(err) } enb, err := newENB(cfg) if err != nil { log.Printf("failed to initialize eNB: %s", err) } defer enb.close() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGHUP) ctx, cancel := context.WithCancel(context.Background()) defer cancel() fatalCh := make(chan error, 1) go func() { if err := enb.run(ctx); err != nil { fatalCh <- err } }() for { select { case sig := <-sigCh: switch sig { case syscall.SIGINT: return case syscall.SIGHUP: // reload config and attach/detach subscribers again newCfg, err := loadConfig(*configPath) if err != nil { log.Printf("Error reloading config %s", err) } if err := enb.reload(newCfg); err != nil { log.Printf("Error applying reloaded config %s", err) } } case err := <-enb.errCh: log.Printf("WARN: %s", err) case err := <-fatalCh: log.Printf("FATAL: %s", err) return } } } ================================================ FILE: examples/gw-tester/enb/metrics.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/vishvananda/netlink" ) type metricsCollector struct { activeSessions prometheus.GaugeFunc activeBearers prometheus.GaugeFunc messagesSent *prometheus.CounterVec messagesReceived *prometheus.CounterVec } func (e *enb) runMetricsCollector() error { mc := &metricsCollector{} mc.activeSessions = promauto.NewGaugeFunc( prometheus.GaugeOpts{ Name: "enb_active_sessions", Help: "number of session established currently", }, func() float64 { return float64(len(e.sessions)) }, ) mc.activeBearers = promauto.NewGaugeFunc( prometheus.GaugeOpts{ Name: "enb_active_bearers", Help: "number of GTP-U tunnels established currently", }, func() float64 { tunnels, err := netlink.GTPPDPList() if err != nil { log.Printf("metrics: could not get tunnels: %s", err) return 0 } return float64(len(tunnels)) }, ) mc.messagesSent = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "enb_messages_sent_total", Help: "number of message sent by messagge type", }, []string{"dst", "type"}, ) mc.messagesReceived = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "enb_messages_received_total", Help: "number of message received by messagge type", }, []string{"src", "type"}, ) e.mc = mc return nil } ================================================ FILE: examples/gw-tester/mme/config.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "io/ioutil" "gopkg.in/yaml.v2" ) // Config is a configurations loaded from yaml. type Config struct { LocalAddrs struct { S1CAddr string `yaml:"s1c_addr"` S11IP string `yaml:"s11_ip"` } `yaml:"local_addresses"` PromAddr string `yaml:"prom_addr"` MCC string `yaml:"mcc"` MNC string `yaml:"mnc"` APN string `yaml:"apn"` SgwS11 string `yaml:"sgw_s11_ip"` PgwS5C string `yaml:"pgw_s5c_ip"` } func loadConfig(path string) (*Config, error) { buf, err := ioutil.ReadFile(path) if err != nil { return nil, err } c := &Config{} if err := yaml.Unmarshal(buf, c); err != nil { return nil, err } return c, nil } ================================================ FILE: examples/gw-tester/mme/handlers.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "fmt" "log" "net" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) func (m *mme) handleCreateSessionResponse(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), sgwAddr) if m.mc != nil { m.mc.messagesReceived.WithLabelValues(sgwAddr.String(), msg.MessageTypeName()).Inc() } // find the session associated with TEID session, err := c.GetSessionByTEID(msg.TEID(), sgwAddr) if err != nil { c.RemoveSession(session) return err } bearer := session.GetDefaultBearer() // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). csRspFromSGW := msg.(*message.CreateSessionResponse) // check Cause value first. if causeIE := csRspFromSGW.Cause; causeIE != nil { cause, err := causeIE.Cause() if err != nil { return err } if cause != gtpv2.CauseRequestAccepted { c.RemoveSession(session) return >pv2.CauseNotOKError{ MsgType: csRspFromSGW.MessageTypeName(), Cause: cause, Msg: fmt.Sprintf("subscriber: %s", session.IMSI), } } } else { return >pv2.RequiredIEMissingError{Type: msg.MessageType()} } if paaIE := csRspFromSGW.PAA; paaIE != nil { bearer.SubscriberIP, err = paaIE.IPAddress() if err != nil { return err } } if fteidcIE := csRspFromSGW.SenderFTEIDC; fteidcIE != nil { teid, err := fteidcIE.TEID() if err != nil { return err } session.AddTEID(gtpv2.IFTypeS11S4SGWGTPC, teid) } else { return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } s11sgwTEID, err := session.GetTEID(gtpv2.IFTypeS11S4SGWGTPC) if err != nil { c.RemoveSession(session) return err } s11mmeTEID, err := session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { c.RemoveSession(session) return err } if brCtxIE := csRspFromSGW.BearerContextsCreated; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.EPSBearerID: bearer.EBI, err = childIE.EPSBearerID() if err != nil { return err } case ie.FullyQualifiedTEID: if childIE.Instance() != 0 { continue } it, err := childIE.InterfaceType() if err != nil { return err } teid, err := childIE.TEID() if err != nil { return err } session.AddTEID(it, teid) } } } else { return >pv2.RequiredIEMissingError{Type: ie.BearerContext} } if err := session.Activate(); err != nil { c.RemoveSession(session) return err } log.Printf( "Session created with S-GW for Subscriber: %s;\n\tS11 S-GW: %s, TEID->: %#x, TEID<-: %#x", session.Subscriber.IMSI, sgwAddr, s11sgwTEID, s11mmeTEID, ) m.created <- struct{}{} return nil } func (m *mme) handleModifyBearerResponse(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), sgwAddr) if m.mc != nil { m.mc.messagesReceived.WithLabelValues(sgwAddr.String(), msg.MessageTypeName()).Inc() } session, err := c.GetSessionByTEID(msg.TEID(), sgwAddr) if err != nil { return err } mbRspFromSGW := msg.(*message.ModifyBearerResponse) if causeIE := mbRspFromSGW.Cause; causeIE != nil { cause, err := causeIE.Cause() if err != nil { return err } if cause != gtpv2.CauseRequestAccepted { return >pv2.CauseNotOKError{ MsgType: msg.MessageTypeName(), Cause: cause, Msg: fmt.Sprintf("subscriber: %s", session.IMSI), } } } else { return >pv2.RequiredIEMissingError{Type: ie.Cause} } if brCtxIE := mbRspFromSGW.BearerContextsModified; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.FullyQualifiedTEID: if childIE.Instance() != 0 { continue } it, err := childIE.InterfaceType() if err != nil { return err } teid, err := childIE.TEID() if err != nil { return err } session.AddTEID(it, teid) m.sgw.s1uIP, err = childIE.IPAddress() if err != nil { return err } } } } else { return >pv2.RequiredIEMissingError{Type: ie.BearerContext} } log.Printf("Bearer modified with S-GW for Subscriber: %s", session.IMSI) m.modified <- struct{}{} return nil } func (m *mme) handleDeleteSessionResponse(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), sgwAddr) if m.mc != nil { m.mc.messagesReceived.WithLabelValues(sgwAddr.String(), msg.MessageTypeName()).Inc() } session, err := c.GetSessionByTEID(msg.TEID(), sgwAddr) if err != nil { return err } c.RemoveSession(session) log.Printf("Session deleted with S-GW for Subscriber: %s", session.IMSI) return nil } ================================================ FILE: examples/gw-tester/mme/main.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Command mme works as pseudo MME/HSS communicates S/P-GW with GTPv2 signaling. package main import ( "context" "flag" "log" "os" "os/signal" "syscall" ) func main() { var configPath = flag.String("config", "./mme.yml", "Path to the configuration file.") flag.Parse() log.SetPrefix("[MME] ") sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGHUP) cfg, err := loadConfig(*configPath) if err != nil { log.Fatal(err) } mme, err := newMME(cfg) if err != nil { log.Printf("failed to initialize MME: %s", err) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() fatalCh := make(chan error) go func() { if err := mme.run(ctx); err != nil { fatalCh <- err } }() for { select { case sig := <-sigCh: switch sig { case syscall.SIGINT: cancel() return case syscall.SIGHUP: // reload config and attach/detach subscribers newCfg, err := loadConfig(*configPath) if err != nil { log.Printf("Error reloading config %s", err) } if err := mme.reload(newCfg); err != nil { log.Printf("Error applying reloaded config %s", err) } } case err := <-mme.errCh: log.Printf("WARN: %s", err) case err := <-fatalCh: log.Printf("FATAL: %s", err) return } } } ================================================ FILE: examples/gw-tester/mme/metrics.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ) type metricsCollector struct { activeSessions prometheus.GaugeFunc messagesSent *prometheus.CounterVec messagesReceived *prometheus.CounterVec } func (m *mme) runMetricsCollector() error { mc := &metricsCollector{} mc.activeSessions = promauto.NewGaugeFunc( prometheus.GaugeOpts{ Name: "mme_active_sessions", Help: "number of session established currently", }, func() float64 { return float64(m.s11Conn.SessionCount()) }, ) mc.messagesSent = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "mme_messages_sent_total", Help: "number of message sent by messagge type", }, []string{"dst", "type"}, ) mc.messagesReceived = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "mme_messages_received_total", Help: "number of message received by messagge type", }, []string{"src", "type"}, ) m.mc = mc return nil } ================================================ FILE: examples/gw-tester/mme/mme.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "context" "fmt" "log" "net" "net/http" "time" "github.com/prometheus/client_golang/prometheus/promhttp" "google.golang.org/grpc" "github.com/wmnsk/go-gtp/examples/gw-tester/s1mme" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) // Session represents a subscriber. type Session struct { IMSI string MSISDN string IMEISV string SrcIP string itei uint32 } type mme struct { s1mmeListener net.Listener s11Addr net.Addr s11IP string s11Conn *gtpv2.Conn created chan struct{} modified chan struct{} apn string mcc, mnc string enb struct { mcc string mnc string tai uint16 eci uint32 s1uIP string } sgw struct { s11IP string s1uIP string } pgw struct { s5cIP string } promAddr string mc *metricsCollector errCh chan error } func newMME(cfg *Config) (*mme, error) { m := &mme{ mcc: cfg.MCC, mnc: cfg.MNC, apn: cfg.APN, created: make(chan struct{}, 1), modified: make(chan struct{}, 1), errCh: make(chan error, 1), } m.sgw.s11IP = cfg.SgwS11 m.pgw.s5cIP = cfg.PgwS5C // setup S11 conn var err error m.s11Addr, err = net.ResolveUDPAddr("udp", cfg.LocalAddrs.S11IP+gtpv2.GTPCPort) if err != nil { return nil, err } m.s11IP = cfg.LocalAddrs.S11IP // setup gRPC server m.s1mmeListener, err = net.Listen("tcp", cfg.LocalAddrs.S1CAddr) if err != nil { return nil, err } if cfg.PromAddr != "" { // validate if the address is valid or not. if _, err = net.ResolveTCPAddr("tcp", cfg.PromAddr); err != nil { return nil, err } m.promAddr = cfg.PromAddr } return m, nil } func (m *mme) run(ctx context.Context) error { fatalCh := make(chan error, 1) srv := grpc.NewServer() s1mme.RegisterAttacherServer(srv, m) go func() { if err := srv.Serve(m.s1mmeListener); err != nil { fatalCh <- fmt.Errorf("error on serving gRPC: %w", err) return } }() log.Printf("Started serving S1-MME on: %s", m.s1mmeListener.Addr()) m.s11Conn = gtpv2.NewConn(m.s11Addr, gtpv2.IFTypeS11MMEGTPC, 0) go func() { if err := m.s11Conn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() log.Printf("Started serving S11 on: %s", m.s11Addr) m.s11Conn.AddHandlers(map[uint8]gtpv2.HandlerFunc{ message.MsgTypeCreateSessionResponse: m.handleCreateSessionResponse, message.MsgTypeModifyBearerResponse: m.handleModifyBearerResponse, message.MsgTypeDeleteSessionResponse: m.handleDeleteSessionResponse, }) // start serving Prometheus, if address is given if m.promAddr != "" { if err := m.runMetricsCollector(); err != nil { return err } http.Handle("/metrics", promhttp.Handler()) go func() { if err := http.ListenAndServe(m.promAddr, nil); err != nil { log.Println(err) } }() log.Printf("Started serving Prometheus on %s", m.promAddr) } for { select { case <-ctx.Done(): // srv.Serve returns when lis is closed if err := m.s1mmeListener.Close(); err != nil { return err } return nil case err := <-fatalCh: return err } } } func (m *mme) reload(cfg *Config) error { // TODO: implement return nil } // Attach is called by eNB by gRPC. func (m *mme) Attach(ctx context.Context, req *s1mme.AttachRequest) (*s1mme.AttachResponse, error) { sess := &Session{ IMSI: req.Imsi, MSISDN: req.Msisdn, IMEISV: req.Imeisv, SrcIP: req.SrcIp, itei: req.ITei, } var err error m.enb.s1uIP, _, err = net.SplitHostPort(req.S1UAddr) if err != nil { return nil, err } errCh := make(chan error, 1) rspCh := make(chan *s1mme.AttachResponse) go func() { m.enb.mcc = req.Location.Mcc m.enb.mnc = req.Location.Mnc m.enb.tai = uint16(req.Location.Tai) m.enb.eci = req.Location.Eci session, err := m.CreateSession(sess) if err != nil { errCh <- err return } log.Printf("Sent Create Session Request for %s", session.IMSI) select { case <-m.created: // go forward case <-time.After(5 * time.Second): errCh <- fmt.Errorf("timed out: %s", session.IMSI) } if _, err = m.ModifyBearer(session, sess); err != nil { errCh <- err return } log.Printf("Sent Modify Bearer Request for %s", session.IMSI) select { case <-m.modified: // go forward case <-time.After(5 * time.Second): errCh <- fmt.Errorf("timed out: %s", session.IMSI) } s1teid, err := session.GetTEID(gtpv2.IFTypeS1USGWGTPU) if err != nil { errCh <- err return } rspCh <- &s1mme.AttachResponse{ Cause: s1mme.Cause_SUCCESS, SgwAddr: m.sgw.s1uIP + gtpv2.GTPUPort, OTei: s1teid, } }() select { case err := <-errCh: return nil, err case rsp := <-rspCh: return rsp, nil } } // Detach is called by eNB by gRPC. func (m *mme) Detach(ctx context.Context, req *s1mme.DetachRequest) (*s1mme.DetachResponse, error) { // TODO: implement return nil, nil } func (m *mme) CreateSession(sess *Session) (*gtpv2.Session, error) { br := gtpv2.NewBearer(5, "", >pv2.QoSProfile{ PL: 2, QCI: 255, MBRUL: 0xffffffff, MBRDL: 0xffffffff, GBRUL: 0xffffffff, GBRDL: 0xffffffff, }) var pci, pvi uint8 if br.PCI { pci = 1 } if br.PVI { pvi = 1 } raddr, err := net.ResolveUDPAddr("udp", m.sgw.s11IP+gtpv2.GTPCPort) if err != nil { return nil, err } session, _, err := m.s11Conn.CreateSession( raddr, ie.NewIMSI(sess.IMSI), ie.NewMSISDN(sess.MSISDN), ie.NewMobileEquipmentIdentity(sess.IMEISV), ie.NewUserLocationInformationStruct( ie.NewCGI(m.enb.mcc, m.enb.mnc, 0x1111, 0x2222), ie.NewSAI(m.enb.mcc, m.enb.mnc, 0x1111, 0x3333), ie.NewRAI(m.enb.mcc, m.enb.mnc, 0x1111, 0x4444), ie.NewTAI(m.enb.mcc, m.enb.mnc, 0x5555), ie.NewECGI(m.enb.mcc, m.enb.mnc, 0x66666666), ie.NewLAI(m.enb.mcc, m.enb.mnc, 0x1111), ie.NewMENBI(m.enb.mcc, m.enb.mnc, 0x11111111), ie.NewEMENBI(m.enb.mcc, m.enb.mnc, 0x22222222), ), ie.NewRATType(gtpv2.RATTypeEUTRAN), ie.NewIndicationFromOctets(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), m.s11Conn.NewSenderFTEID(m.s11IP, ""), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, 0, m.pgw.s5cIP, "").WithInstance(1), ie.NewAccessPointName(m.apn), ie.NewSelectionMode(gtpv2.SelectionModeMSorNetworkProvidedAPNSubscribedVerified), ie.NewPDNType(gtpv2.PDNTypeIPv4), ie.NewPDNAddressAllocation(sess.SrcIP), ie.NewAPNRestriction(gtpv2.APNRestrictionNoExistingContextsorRestriction), ie.NewAggregateMaximumBitRate(0, 0), ie.NewBearerContext( ie.NewEPSBearerID(br.EBI), ie.NewBearerQoS(pci, br.PL, pvi, br.QCI, br.MBRUL, br.MBRDL, br.GBRUL, br.GBRDL), ), ie.NewFullyQualifiedCSID(m.s11IP, 1), ie.NewServingNetwork(m.mcc, m.mnc), ie.NewUETimeZone(9*time.Hour, 0), ) if err != nil { return nil, err } if m.mc != nil { m.mc.messagesSent.WithLabelValues(raddr.String(), "Create Session Request").Inc() } return session, nil } func (m *mme) ModifyBearer(sess *gtpv2.Session, sub *Session) (*gtpv2.Bearer, error) { teid, err := sess.GetTEID(gtpv2.IFTypeS11S4SGWGTPC) if err != nil { return nil, err } fteid := ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, sub.itei, m.enb.s1uIP, "") if _, err = m.s11Conn.ModifyBearer( teid, sess, ie.NewIndicationFromOctets(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), ie.NewBearerContext(ie.NewEPSBearerID(sess.GetDefaultBearer().EBI), fteid, ie.NewPortNumber(2125)), ); err != nil { return nil, err } if m.mc != nil { m.mc.messagesSent.WithLabelValues(sess.PeerAddr().String(), "Modify Bearer Request").Inc() } return nil, nil } ================================================ FILE: examples/gw-tester/mme/mme.yml ================================================ mcc: "001" mnc: "01" apn: "gw-tester.go-gtp.example" local_addresses: s1c_addr: "127.0.1.12:36412" s11_ip: "127.0.1.12" sgw_s11_ip: "127.0.1.13" pgw_s5c_ip: "127.0.1.15" prom_addr: "127.0.10.2:58080" ================================================ FILE: examples/gw-tester/pgw/config.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "io/ioutil" "gopkg.in/yaml.v2" ) // Config is a configurations loaded from yaml. type Config struct { LocalAddrs struct { S5CIP string `yaml:"s5c_ip"` S5UIP string `yaml:"s5u_ip"` SGiIP string `yaml:"sgi_ip"` } `yaml:"local_addresses"` UseKernelGTP bool `yaml:"use_kernel_gtp"` SGiIFName string `yaml:"sgi_if_name"` RouteSubnet string `yaml:"route_subnet"` PromAddr string `yaml:"prom_addr"` } func loadConfig(path string) (*Config, error) { buf, err := ioutil.ReadFile(path) if err != nil { return nil, err } c := &Config{} if err := yaml.Unmarshal(buf, c); err != nil { return nil, err } return c, nil } ================================================ FILE: examples/gw-tester/pgw/handlers.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "fmt" "log" "net" "strings" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) func (p *pgw) handleCreateSessionRequest(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), sgwAddr) if p.mc != nil { p.mc.messagesReceived.WithLabelValues(sgwAddr.String(), msg.MessageTypeName()).Inc() } // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). csReqFromSGW := msg.(*message.CreateSessionRequest) // keep session information retrieved from the message. session := gtpv2.NewSession(sgwAddr, >pv2.Subscriber{Location: >pv2.Location{}}) bearer := session.GetDefaultBearer() var err error if imsiIE := csReqFromSGW.IMSI; imsiIE != nil { imsi, err := imsiIE.IMSI() if err != nil { return err } session.IMSI = imsi // remove previous session for the same subscriber if exists. sess, err := c.GetSessionByIMSI(imsi) if err != nil { switch err.(type) { case *gtpv2.UnknownIMSIError: // whole new session. just ignore. default: return fmt.Errorf("got something unexpected: %w", err) } } else { c.RemoveSession(sess) } } else { return >pv2.RequiredIEMissingError{Type: ie.IMSI} } if msisdnIE := csReqFromSGW.MSISDN; msisdnIE != nil { session.MSISDN, err = msisdnIE.MSISDN() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.MSISDN} } if meiIE := csReqFromSGW.MEI; meiIE != nil { session.IMEI, err = meiIE.MobileEquipmentIdentity() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.MobileEquipmentIdentity} } if apnIE := csReqFromSGW.APN; apnIE != nil { bearer.APN, err = apnIE.AccessPointName() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.AccessPointName} } if netIE := csReqFromSGW.ServingNetwork; netIE != nil { session.MCC, err = netIE.MCC() if err != nil { return err } session.MNC, err = netIE.MNC() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.ServingNetwork} } if ratIE := csReqFromSGW.RATType; ratIE != nil { session.RATType, err = ratIE.RATType() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.RATType} } if fteidcIE := csReqFromSGW.SenderFTEIDC; fteidcIE != nil { teid, err := fteidcIE.TEID() if err != nil { return err } session.AddTEID(gtpv2.IFTypeS5S8SGWGTPC, teid) } else { return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } var s5sgwuIP string var oteiU uint32 if brCtxIE := csReqFromSGW.BearerContextsToBeCreated; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.EPSBearerID: bearer.EBI, err = childIE.EPSBearerID() if err != nil { return err } case ie.FullyQualifiedTEID: it, err := childIE.InterfaceType() if err != nil { return err } oteiU, err = childIE.TEID() if err != nil { return err } session.AddTEID(it, oteiU) s5sgwuIP, err = childIE.IPAddress() if err != nil { return err } } } } else { return >pv2.RequiredIEMissingError{Type: ie.BearerContext} } if paaIE := csReqFromSGW.PAA; paaIE != nil { bearer.SubscriberIP, err = paaIE.IPAddress() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.PDNAddressAllocation} } cIP := strings.Split(c.LocalAddr().String(), ":")[0] uIP := strings.Split(p.s5u, ":")[0] s5cFTEID := c.NewSenderFTEID(cIP, "").WithInstance(1) s5uFTEID := p.uConn.NewFTEID(gtpv2.IFTypeS5S8PGWGTPU, uIP, "").WithInstance(2) s5sgwTEID, err := session.GetTEID(gtpv2.IFTypeS5S8SGWGTPC) if err != nil { return err } csRspFromPGW := message.NewCreateSessionResponse( s5sgwTEID, 0, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), s5cFTEID, ie.NewPDNAddressAllocation(bearer.SubscriberIP), ie.NewAPNRestriction(gtpv2.APNRestrictionPublic2), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(bearer.EBI), s5uFTEID, ie.NewChargingID(bearer.ChargingID), ), ) if csReqFromSGW.SGWFQCSID != nil { csRspFromPGW.PGWFQCSID = ie.NewFullyQualifiedCSID(cIP, 1) } session.AddTEID(gtpv2.IFTypeS5S8PGWGTPC, s5cFTEID.MustTEID()) session.AddTEID(gtpv2.IFTypeS5S8PGWGTPU, s5uFTEID.MustTEID()) if err := c.RespondTo(sgwAddr, csReqFromSGW, csRspFromPGW); err != nil { return err } if p.mc != nil { p.mc.messagesSent.WithLabelValues(sgwAddr.String(), csRspFromPGW.MessageTypeName()).Inc() } s5pgwTEID, err := session.GetTEID(gtpv2.IFTypeS5S8PGWGTPC) if err != nil { return err } // don't forget to activate and add session created to the session list if err := session.Activate(); err != nil { return err } c.RegisterSession(s5pgwTEID, session) if p.useKernelGTP { if err := p.setupUPlane(net.ParseIP(s5sgwuIP), net.ParseIP(bearer.SubscriberIP), oteiU, s5uFTEID.MustTEID()); err != nil { return err } } log.Printf("Session created with S-GW for subscriber: %s;\n\tS5C S-GW: %s, TEID->: %#x, TEID<-: %#x", session.Subscriber.IMSI, sgwAddr, s5sgwTEID, s5pgwTEID, ) return nil } func (p *pgw) handleDeleteSessionRequest(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), sgwAddr) if p.mc != nil { p.mc.messagesReceived.WithLabelValues(sgwAddr.String(), msg.MessageTypeName()).Inc() } // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). session, err := c.GetSessionByTEID(msg.TEID(), sgwAddr) if err != nil { dsr := message.NewDeleteSessionResponse( 0, 0, ie.NewCause(gtpv2.CauseIMSIIMEINotKnown, 0, 0, 0, nil), ) if err := c.RespondTo(sgwAddr, msg, dsr); err != nil { return err } return err } // respond to S-GW with DeleteSessionResponse. teid, err := session.GetTEID(gtpv2.IFTypeS5S8SGWGTPC) if err != nil { log.Println(err) return nil } dsr := message.NewDeleteSessionResponse( teid, 0, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ) if err := c.RespondTo(sgwAddr, msg, dsr); err != nil { return err } if p.mc != nil { p.mc.messagesSent.WithLabelValues(sgwAddr.String(), dsr.MessageTypeName()).Inc() } log.Printf("Session deleted for Subscriber: %s", session.IMSI) c.RemoveSession(session) return nil } ================================================ FILE: examples/gw-tester/pgw/main.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Command pgw is a dead simple implementation of P-GW only with GTP-related features. package main import ( "context" "flag" "log" "os" "os/signal" "syscall" ) func main() { var configPath = flag.String("config", "./pgw.yml", "Path to the configuration file.") flag.Parse() log.SetPrefix("[P-GW] ") cfg, err := loadConfig(*configPath) if err != nil { log.Println(err) return } pgw, err := newPGW(cfg) if err != nil { log.Printf("failed to initialize P-GW: %s", err) return } defer pgw.close() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGHUP) ctx, cancel := context.WithCancel(context.Background()) defer cancel() fatalCh := make(chan error) go func() { if err := pgw.run(ctx); err != nil { fatalCh <- err } }() for { select { case sig := <-sigCh: // TODO: reload config on receiving SIGHUP log.Println(sig) return case err := <-pgw.errCh: log.Printf("WARN: %s", err) case err := <-fatalCh: log.Printf("FATAL: %s", err) return } } } ================================================ FILE: examples/gw-tester/pgw/metrics.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/vishvananda/netlink" ) type metricsCollector struct { activeSessions prometheus.GaugeFunc activeBearers prometheus.GaugeFunc messagesSent *prometheus.CounterVec messagesReceived *prometheus.CounterVec } func (p *pgw) runMetricsCollector() error { mc := &metricsCollector{} mc.activeSessions = promauto.NewGaugeFunc( prometheus.GaugeOpts{ Name: "pgw_active_sessions", Help: "number of session established currently", }, func() float64 { return float64(p.cConn.SessionCount()) }, ) mc.activeBearers = promauto.NewGaugeFunc( prometheus.GaugeOpts{ Name: "pgw_active_bearers", Help: "number of GTP-U tunnels established currently", }, func() float64 { tunnels, err := netlink.GTPPDPList() if err != nil { log.Printf("metrics: could not get tunnels: %s", err) return 0 } return float64(len(tunnels)) }, ) mc.messagesSent = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "pgw_messages_sent_total", Help: "number of message sent by messagge type", }, []string{"dst", "type"}, ) mc.messagesReceived = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "pgw_messages_received_total", Help: "number of message received by messagge type", }, []string{"src", "type"}, ) p.mc = mc return nil } ================================================ FILE: examples/gw-tester/pgw/pgw.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "context" "fmt" "log" "net" "net/http" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/vishvananda/netlink" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/message" ) type pgw struct { cConn *gtpv2.Conn uConn *gtpv1.UPlaneConn s5c, s5u string sgiIF string useKernelGTP bool routeSubnet *net.IPNet addedRoutes []*netlink.Route addedRules []*netlink.Rule promAddr string mc *metricsCollector errCh chan error } func newPGW(cfg *Config) (*pgw, error) { p := &pgw{ s5c: cfg.LocalAddrs.S5CIP + gtpv2.GTPCPort, s5u: cfg.LocalAddrs.S5UIP + gtpv2.GTPUPort, useKernelGTP: cfg.UseKernelGTP, sgiIF: cfg.SGiIFName, errCh: make(chan error, 1), } var err error _, p.routeSubnet, err = net.ParseCIDR(cfg.RouteSubnet) if err != nil { return nil, err } if cfg.PromAddr != "" { // validate if the address is valid or not. if _, err = net.ResolveTCPAddr("tcp", cfg.PromAddr); err != nil { return nil, err } p.promAddr = cfg.PromAddr } if !p.useKernelGTP { log.Println("WARN: U-Plane does not work without GTP kernel module") } return p, nil } func (p *pgw) run(ctx context.Context) error { cAddr, err := net.ResolveUDPAddr("udp", p.s5c) if err != nil { return err } p.cConn = gtpv2.NewConn(cAddr, gtpv2.IFTypeS5S8PGWGTPC, 0) go func() { if err := p.cConn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() log.Printf("Started serving S5-C on %s", cAddr) // register handlers for ALL the message you expect remote endpoint to send. p.cConn.AddHandlers(map[uint8]gtpv2.HandlerFunc{ message.MsgTypeCreateSessionRequest: p.handleCreateSessionRequest, message.MsgTypeDeleteSessionRequest: p.handleDeleteSessionRequest, }) uAddr, err := net.ResolveUDPAddr("udp", p.s5u) if err != nil { return err } p.uConn = gtpv1.NewUPlaneConn(uAddr) if p.useKernelGTP { if err := p.uConn.EnableKernelGTP("gtp-pgw", gtpv1.RoleGGSN); err != nil { return err } } go func() { if err = p.uConn.ListenAndServe(ctx); err != nil { log.Println(err) return } log.Println("uConn.ListenAndServe exitted") }() log.Printf("Started serving S5-U on %s", uAddr) // start serving Prometheus, if address is given if p.promAddr != "" { if err := p.runMetricsCollector(); err != nil { return err } http.Handle("/metrics", promhttp.Handler()) go func() { if err := http.ListenAndServe(p.promAddr, nil); err != nil { log.Println(err) } }() log.Printf("Started serving Prometheus on %s", p.promAddr) } for { select { case <-ctx.Done(): return nil case err := <-p.errCh: log.Printf("Warning: %s", err) } } } func (p *pgw) close() error { var errs []error for _, r := range p.addedRoutes { if err := netlink.RouteDel(r); err != nil { errs = append(errs, err) } } for _, r := range p.addedRules { if err := netlink.RuleDel(r); err != nil { errs = append(errs, err) } } if p.uConn != nil { if err := p.uConn.Close(); err != nil { errs = append(errs, err) } } if err := p.cConn.Close(); err != nil { errs = append(errs, err) } close(p.errCh) if len(errs) > 0 { return fmt.Errorf("errors while closing S-GW: %+v", errs) } return nil } func (p *pgw) setupUPlane(peerIP, msIP net.IP, otei, itei uint32) error { if err := p.uConn.AddTunnelOverride(peerIP, msIP, otei, itei); err != nil { return err } ms32 := &net.IPNet{IP: msIP, Mask: net.CIDRMask(32, 32)} dlroute := &netlink.Route{ // ip route replace Dst: ms32, // UE's IP LinkIndex: p.uConn.KernelGTP.Link.Attrs().Index, // dev gtp-pgw Scope: netlink.SCOPE_LINK, // scope link Protocol: 4, // proto static Priority: 1, // metric 1 Table: 3001, // table 3001 } if err := netlink.RouteReplace(dlroute); err != nil { return err } p.addedRoutes = append(p.addedRoutes, dlroute) link, err := netlink.LinkByName(p.sgiIF) if err != nil { return err } ulroute := &netlink.Route{ // ip route replace Dst: p.routeSubnet, // dst network via SGi LinkIndex: link.Attrs().Index, // SGi I/F name Scope: netlink.SCOPE_LINK, // scope link Protocol: 4, // proto static Priority: 1, // metric 1 } if err := netlink.RouteReplace(ulroute); err != nil { return err } p.addedRoutes = append(p.addedRoutes, ulroute) rules, err := netlink.RuleList(0) if err != nil { return err } for _, r := range rules { if r.IifName == link.Attrs().Name && r.Dst == ms32 { return nil } } rule := netlink.NewRule() rule.IifName = link.Attrs().Name rule.Dst = ms32 rule.Table = 3001 if err := netlink.RuleAdd(rule); err != nil { return err } p.addedRules = append(p.addedRules, rule) return nil } ================================================ FILE: examples/gw-tester/pgw/pgw.yml ================================================ local_addresses: s5c_ip: "127.0.1.15" s5u_ip: "127.0.0.15" sgi_ip: "127.0.1.254" sgi_if_name: "lo" route_subnet: "192.168.101.0/24" use_kernel_gtp: false prom_addr: "127.0.10.4:58080" ================================================ FILE: examples/gw-tester/s1mme/s1mme.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // source: s1mme.proto package s1mme import ( context "context" fmt "fmt" proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" math "math" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package // Cause represents a result of attach / detach. type Cause int32 const ( Cause_INVALID Cause = 0 Cause_SUCCESS Cause = 1 Cause_GW_UNAVAILABLE Cause = 2 ) var Cause_name = map[int32]string{ 0: "INVALID", 1: "SUCCESS", 2: "GW_UNAVAILABLE", } var Cause_value = map[string]int32{ "INVALID": 0, "SUCCESS": 1, "GW_UNAVAILABLE": 2, } func (x Cause) String() string { return proto.EnumName(Cause_name, int32(x)) } func (Cause) EnumDescriptor() ([]byte, []int) { return fileDescriptor_24365dc18e89382c, []int{0} } type Location_RATType int32 const ( Location_INVALID Location_RATType = 0 Location_UTRAN Location_RATType = 1 Location_GERAN Location_RATType = 2 Location_WLAN Location_RATType = 3 Location_GAN Location_RATType = 4 Location_HSPA_EVOLUTION Location_RATType = 5 Location_EUTRAN Location_RATType = 6 Location_VIRTUAL Location_RATType = 7 Location_EUTRAN_NB_IOT Location_RATType = 8 Location_LTEM Location_RATType = 9 Location_NR Location_RATType = 10 ) var Location_RATType_name = map[int32]string{ 0: "INVALID", 1: "UTRAN", 2: "GERAN", 3: "WLAN", 4: "GAN", 5: "HSPA_EVOLUTION", 6: "EUTRAN", 7: "VIRTUAL", 8: "EUTRAN_NB_IOT", 9: "LTEM", 10: "NR", } var Location_RATType_value = map[string]int32{ "INVALID": 0, "UTRAN": 1, "GERAN": 2, "WLAN": 3, "GAN": 4, "HSPA_EVOLUTION": 5, "EUTRAN": 6, "VIRTUAL": 7, "EUTRAN_NB_IOT": 8, "LTEM": 9, "NR": 10, } func (x Location_RATType) String() string { return proto.EnumName(Location_RATType_name, int32(x)) } func (Location_RATType) EnumDescriptor() ([]byte, []int) { return fileDescriptor_24365dc18e89382c, []int{2, 0} } // AttachRequest is used to request MME to create a session/bearer. type AttachRequest struct { Imsi string `protobuf:"bytes,1,opt,name=imsi,proto3" json:"imsi,omitempty"` Msisdn string `protobuf:"bytes,2,opt,name=msisdn,proto3" json:"msisdn,omitempty"` Imeisv string `protobuf:"bytes,3,opt,name=imeisv,proto3" json:"imeisv,omitempty"` S1UAddr string `protobuf:"bytes,4,opt,name=s1u_addr,json=s1uAddr,proto3" json:"s1u_addr,omitempty"` SrcIp string `protobuf:"bytes,5,opt,name=src_ip,json=srcIp,proto3" json:"src_ip,omitempty"` ITei uint32 `protobuf:"varint,6,opt,name=i_tei,json=iTei,proto3" json:"i_tei,omitempty"` Location *Location `protobuf:"bytes,7,opt,name=location,proto3" json:"location,omitempty"` Reattach bool `protobuf:"varint,8,opt,name=reattach,proto3" json:"reattach,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *AttachRequest) Reset() { *m = AttachRequest{} } func (m *AttachRequest) String() string { return proto.CompactTextString(m) } func (*AttachRequest) ProtoMessage() {} func (*AttachRequest) Descriptor() ([]byte, []int) { return fileDescriptor_24365dc18e89382c, []int{0} } func (m *AttachRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_AttachRequest.Unmarshal(m, b) } func (m *AttachRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_AttachRequest.Marshal(b, m, deterministic) } func (m *AttachRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_AttachRequest.Merge(m, src) } func (m *AttachRequest) XXX_Size() int { return xxx_messageInfo_AttachRequest.Size(m) } func (m *AttachRequest) XXX_DiscardUnknown() { xxx_messageInfo_AttachRequest.DiscardUnknown(m) } var xxx_messageInfo_AttachRequest proto.InternalMessageInfo func (m *AttachRequest) GetImsi() string { if m != nil { return m.Imsi } return "" } func (m *AttachRequest) GetMsisdn() string { if m != nil { return m.Msisdn } return "" } func (m *AttachRequest) GetImeisv() string { if m != nil { return m.Imeisv } return "" } func (m *AttachRequest) GetS1UAddr() string { if m != nil { return m.S1UAddr } return "" } func (m *AttachRequest) GetSrcIp() string { if m != nil { return m.SrcIp } return "" } func (m *AttachRequest) GetITei() uint32 { if m != nil { return m.ITei } return 0 } func (m *AttachRequest) GetLocation() *Location { if m != nil { return m.Location } return nil } func (m *AttachRequest) GetReattach() bool { if m != nil { return m.Reattach } return false } // AttachResponse is used to respond to AttachRequest. type AttachResponse struct { Cause Cause `protobuf:"varint,1,opt,name=cause,proto3,enum=s1mme.Cause" json:"cause,omitempty"` SgwAddr string `protobuf:"bytes,2,opt,name=sgw_addr,json=sgwAddr,proto3" json:"sgw_addr,omitempty"` OTei uint32 `protobuf:"varint,3,opt,name=o_tei,json=oTei,proto3" json:"o_tei,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *AttachResponse) Reset() { *m = AttachResponse{} } func (m *AttachResponse) String() string { return proto.CompactTextString(m) } func (*AttachResponse) ProtoMessage() {} func (*AttachResponse) Descriptor() ([]byte, []int) { return fileDescriptor_24365dc18e89382c, []int{1} } func (m *AttachResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_AttachResponse.Unmarshal(m, b) } func (m *AttachResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_AttachResponse.Marshal(b, m, deterministic) } func (m *AttachResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_AttachResponse.Merge(m, src) } func (m *AttachResponse) XXX_Size() int { return xxx_messageInfo_AttachResponse.Size(m) } func (m *AttachResponse) XXX_DiscardUnknown() { xxx_messageInfo_AttachResponse.DiscardUnknown(m) } var xxx_messageInfo_AttachResponse proto.InternalMessageInfo func (m *AttachResponse) GetCause() Cause { if m != nil { return m.Cause } return Cause_INVALID } func (m *AttachResponse) GetSgwAddr() string { if m != nil { return m.SgwAddr } return "" } func (m *AttachResponse) GetOTei() uint32 { if m != nil { return m.OTei } return 0 } // Location represents a set of location-related information. type Location struct { Mcc string `protobuf:"bytes,1,opt,name=mcc,proto3" json:"mcc,omitempty"` Mnc string `protobuf:"bytes,2,opt,name=mnc,proto3" json:"mnc,omitempty"` RatType Location_RATType `protobuf:"varint,3,opt,name=rat_type,json=ratType,proto3,enum=s1mme.Location_RATType" json:"rat_type,omitempty"` Tai uint32 `protobuf:"varint,4,opt,name=tai,proto3" json:"tai,omitempty"` Eci uint32 `protobuf:"varint,5,opt,name=eci,proto3" json:"eci,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *Location) Reset() { *m = Location{} } func (m *Location) String() string { return proto.CompactTextString(m) } func (*Location) ProtoMessage() {} func (*Location) Descriptor() ([]byte, []int) { return fileDescriptor_24365dc18e89382c, []int{2} } func (m *Location) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Location.Unmarshal(m, b) } func (m *Location) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Location.Marshal(b, m, deterministic) } func (m *Location) XXX_Merge(src proto.Message) { xxx_messageInfo_Location.Merge(m, src) } func (m *Location) XXX_Size() int { return xxx_messageInfo_Location.Size(m) } func (m *Location) XXX_DiscardUnknown() { xxx_messageInfo_Location.DiscardUnknown(m) } var xxx_messageInfo_Location proto.InternalMessageInfo func (m *Location) GetMcc() string { if m != nil { return m.Mcc } return "" } func (m *Location) GetMnc() string { if m != nil { return m.Mnc } return "" } func (m *Location) GetRatType() Location_RATType { if m != nil { return m.RatType } return Location_INVALID } func (m *Location) GetTai() uint32 { if m != nil { return m.Tai } return 0 } func (m *Location) GetEci() uint32 { if m != nil { return m.Eci } return 0 } // DetachRequest is used to request MME to delete a session/bearer. type DetachRequest struct { Imsi string `protobuf:"bytes,1,opt,name=imsi,proto3" json:"imsi,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DetachRequest) Reset() { *m = DetachRequest{} } func (m *DetachRequest) String() string { return proto.CompactTextString(m) } func (*DetachRequest) ProtoMessage() {} func (*DetachRequest) Descriptor() ([]byte, []int) { return fileDescriptor_24365dc18e89382c, []int{3} } func (m *DetachRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_DetachRequest.Unmarshal(m, b) } func (m *DetachRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_DetachRequest.Marshal(b, m, deterministic) } func (m *DetachRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_DetachRequest.Merge(m, src) } func (m *DetachRequest) XXX_Size() int { return xxx_messageInfo_DetachRequest.Size(m) } func (m *DetachRequest) XXX_DiscardUnknown() { xxx_messageInfo_DetachRequest.DiscardUnknown(m) } var xxx_messageInfo_DetachRequest proto.InternalMessageInfo func (m *DetachRequest) GetImsi() string { if m != nil { return m.Imsi } return "" } // DetachResponse is used to respond to DetachRequest. type DetachResponse struct { Cause Cause `protobuf:"varint,1,opt,name=cause,proto3,enum=s1mme.Cause" json:"cause,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DetachResponse) Reset() { *m = DetachResponse{} } func (m *DetachResponse) String() string { return proto.CompactTextString(m) } func (*DetachResponse) ProtoMessage() {} func (*DetachResponse) Descriptor() ([]byte, []int) { return fileDescriptor_24365dc18e89382c, []int{4} } func (m *DetachResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_DetachResponse.Unmarshal(m, b) } func (m *DetachResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_DetachResponse.Marshal(b, m, deterministic) } func (m *DetachResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_DetachResponse.Merge(m, src) } func (m *DetachResponse) XXX_Size() int { return xxx_messageInfo_DetachResponse.Size(m) } func (m *DetachResponse) XXX_DiscardUnknown() { xxx_messageInfo_DetachResponse.DiscardUnknown(m) } var xxx_messageInfo_DetachResponse proto.InternalMessageInfo func (m *DetachResponse) GetCause() Cause { if m != nil { return m.Cause } return Cause_INVALID } func init() { proto.RegisterEnum("s1mme.Cause", Cause_name, Cause_value) proto.RegisterEnum("s1mme.Location_RATType", Location_RATType_name, Location_RATType_value) proto.RegisterType((*AttachRequest)(nil), "s1mme.AttachRequest") proto.RegisterType((*AttachResponse)(nil), "s1mme.AttachResponse") proto.RegisterType((*Location)(nil), "s1mme.Location") proto.RegisterType((*DetachRequest)(nil), "s1mme.DetachRequest") proto.RegisterType((*DetachResponse)(nil), "s1mme.DetachResponse") } func init() { proto.RegisterFile("s1mme.proto", fileDescriptor_24365dc18e89382c) } var fileDescriptor_24365dc18e89382c = []byte{ // 536 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x4f, 0x6f, 0xd3, 0x4c, 0x10, 0xc6, 0x6b, 0x27, 0xfe, 0xd3, 0xc9, 0xeb, 0xbc, 0xcb, 0x96, 0x82, 0xe9, 0x29, 0x32, 0x97, 0x08, 0xa4, 0x4a, 0x0d, 0x20, 0xce, 0xdb, 0xd4, 0x0a, 0x96, 0x8c, 0x83, 0x36, 0x4e, 0x7a, 0xb4, 0x8c, 0xbd, 0x2a, 0x2b, 0xe1, 0xd8, 0x78, 0x1d, 0xaa, 0x4a, 0x7c, 0x09, 0xae, 0x7c, 0x3d, 0xbe, 0x08, 0xda, 0x5d, 0x27, 0x52, 0x7a, 0x81, 0xdb, 0x33, 0xcf, 0xec, 0x8c, 0xe7, 0x37, 0xde, 0x85, 0x91, 0xb8, 0xaa, 0x2a, 0x76, 0xd9, 0xb4, 0x75, 0x57, 0x63, 0x4b, 0x05, 0xc1, 0x6f, 0x03, 0x3c, 0xd2, 0x75, 0x79, 0xf1, 0x85, 0xb2, 0x6f, 0x3b, 0x26, 0x3a, 0x8c, 0x61, 0xc8, 0x2b, 0xc1, 0x7d, 0x63, 0x62, 0x4c, 0x4f, 0xa9, 0xd2, 0xf8, 0x19, 0xd8, 0x95, 0xe0, 0xa2, 0xdc, 0xfa, 0xa6, 0x72, 0xfb, 0x48, 0xfa, 0xbc, 0x62, 0x5c, 0x7c, 0xf7, 0x07, 0xda, 0xd7, 0x11, 0x7e, 0x01, 0xae, 0xb8, 0xda, 0x65, 0x79, 0x59, 0xb6, 0xfe, 0x50, 0x65, 0x1c, 0x71, 0xb5, 0x23, 0x65, 0xd9, 0xe2, 0x73, 0xb0, 0x45, 0x5b, 0x64, 0xbc, 0xf1, 0x2d, 0x95, 0xb0, 0x44, 0x5b, 0x44, 0x0d, 0x3e, 0x03, 0x8b, 0x67, 0x1d, 0xe3, 0xbe, 0x3d, 0x31, 0xa6, 0x1e, 0x1d, 0xf2, 0x94, 0x71, 0xfc, 0x1a, 0xdc, 0xaf, 0x75, 0x91, 0x77, 0xbc, 0xde, 0xfa, 0xce, 0xc4, 0x98, 0x8e, 0x66, 0xff, 0x5f, 0x6a, 0x86, 0xb8, 0xb7, 0xe9, 0xe1, 0x00, 0xbe, 0x00, 0xb7, 0x65, 0xb9, 0x42, 0xf1, 0xdd, 0x89, 0x31, 0x75, 0xe9, 0x21, 0x0e, 0x4a, 0x18, 0xef, 0x21, 0x45, 0x53, 0x6f, 0x05, 0xc3, 0x01, 0x58, 0x45, 0xbe, 0x13, 0x4c, 0x61, 0x8e, 0x67, 0xff, 0xf5, 0x7d, 0xe7, 0xd2, 0xa3, 0x3a, 0xa5, 0x28, 0xee, 0xee, 0x35, 0x85, 0xd9, 0x53, 0xdc, 0xdd, 0x2b, 0x8a, 0x33, 0xb0, 0x6a, 0x35, 0xee, 0x40, 0x8f, 0x5b, 0xa7, 0x8c, 0x07, 0xbf, 0x4c, 0x70, 0xf7, 0x83, 0x61, 0x04, 0x83, 0xaa, 0x28, 0xfa, 0x2d, 0x4a, 0xa9, 0x9c, 0x6d, 0xd1, 0x77, 0x92, 0x12, 0xcf, 0xc0, 0x6d, 0xf3, 0x2e, 0xeb, 0x1e, 0x1a, 0xa6, 0x1a, 0x8d, 0x67, 0xcf, 0x1f, 0xf1, 0x5d, 0x52, 0x92, 0xa6, 0x0f, 0x0d, 0xa3, 0x4e, 0x9b, 0x77, 0x52, 0xc8, 0x2e, 0x5d, 0xce, 0xd5, 0x56, 0x3d, 0x2a, 0xa5, 0x74, 0x58, 0xc1, 0xd5, 0x3a, 0x3d, 0x2a, 0x65, 0xf0, 0xd3, 0x00, 0xa7, 0x2f, 0xc4, 0x23, 0x70, 0xa2, 0x64, 0x43, 0xe2, 0xe8, 0x06, 0x9d, 0xe0, 0x53, 0xb0, 0xd6, 0x29, 0x25, 0x09, 0x32, 0xa4, 0x5c, 0x84, 0x52, 0x9a, 0xd8, 0x85, 0xe1, 0x6d, 0x4c, 0x12, 0x34, 0xc0, 0x0e, 0x0c, 0x16, 0x24, 0x41, 0x43, 0x8c, 0x61, 0xfc, 0x61, 0xf5, 0x89, 0x64, 0xe1, 0x66, 0x19, 0xaf, 0xd3, 0x68, 0x99, 0x20, 0x0b, 0x03, 0xd8, 0xa1, 0xae, 0xb6, 0x65, 0xd7, 0x4d, 0x44, 0xd3, 0x35, 0x89, 0x91, 0x83, 0x9f, 0x80, 0xa7, 0x13, 0x59, 0x72, 0x9d, 0x45, 0xcb, 0x14, 0xb9, 0xb2, 0x65, 0x9c, 0x86, 0x1f, 0xd1, 0x29, 0xb6, 0xc1, 0x4c, 0x28, 0x82, 0xe0, 0x25, 0x78, 0x37, 0xec, 0x2f, 0xf7, 0x2c, 0x78, 0x0b, 0xe3, 0xfd, 0xa1, 0x7f, 0xff, 0x4f, 0xaf, 0xde, 0x81, 0xa5, 0xe2, 0x63, 0xd6, 0x11, 0x38, 0xab, 0xf5, 0x7c, 0x1e, 0xae, 0x56, 0xc8, 0x90, 0x3c, 0x8b, 0xdb, 0x6c, 0x9d, 0x90, 0x0d, 0x89, 0x62, 0x72, 0x1d, 0x87, 0xc8, 0x9c, 0xfd, 0x00, 0x57, 0x5f, 0x0a, 0xd6, 0xe2, 0xf7, 0x60, 0x6b, 0x8d, 0x9f, 0xf6, 0x5f, 0x38, 0x7a, 0x14, 0x17, 0xe7, 0x8f, 0x5c, 0x3d, 0x5d, 0x70, 0x22, 0x0b, 0xf5, 0xc4, 0x87, 0xc2, 0x23, 0xca, 0x43, 0xe1, 0x31, 0x56, 0x70, 0xf2, 0xd9, 0x56, 0xcf, 0xf0, 0xcd, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x17, 0x91, 0x66, 0x95, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 // AttacherClient is the client API for Attacher service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type AttacherClient interface { Attach(ctx context.Context, in *AttachRequest, opts ...grpc.CallOption) (*AttachResponse, error) Detach(ctx context.Context, in *DetachRequest, opts ...grpc.CallOption) (*DetachResponse, error) } type attacherClient struct { cc *grpc.ClientConn } func NewAttacherClient(cc *grpc.ClientConn) AttacherClient { return &attacherClient{cc} } func (c *attacherClient) Attach(ctx context.Context, in *AttachRequest, opts ...grpc.CallOption) (*AttachResponse, error) { out := new(AttachResponse) err := c.cc.Invoke(ctx, "/s1mme.Attacher/Attach", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *attacherClient) Detach(ctx context.Context, in *DetachRequest, opts ...grpc.CallOption) (*DetachResponse, error) { out := new(DetachResponse) err := c.cc.Invoke(ctx, "/s1mme.Attacher/Detach", in, out, opts...) if err != nil { return nil, err } return out, nil } // AttacherServer is the server API for Attacher service. type AttacherServer interface { Attach(context.Context, *AttachRequest) (*AttachResponse, error) Detach(context.Context, *DetachRequest) (*DetachResponse, error) } // UnimplementedAttacherServer can be embedded to have forward compatible implementations. type UnimplementedAttacherServer struct { } func (*UnimplementedAttacherServer) Attach(ctx context.Context, req *AttachRequest) (*AttachResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Attach not implemented") } func (*UnimplementedAttacherServer) Detach(ctx context.Context, req *DetachRequest) (*DetachResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Detach not implemented") } func RegisterAttacherServer(s *grpc.Server, srv AttacherServer) { s.RegisterService(&_Attacher_serviceDesc, srv) } func _Attacher_Attach_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AttachRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(AttacherServer).Attach(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/s1mme.Attacher/Attach", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(AttacherServer).Attach(ctx, req.(*AttachRequest)) } return interceptor(ctx, in, info, handler) } func _Attacher_Detach_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DetachRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(AttacherServer).Detach(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/s1mme.Attacher/Detach", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(AttacherServer).Detach(ctx, req.(*DetachRequest)) } return interceptor(ctx, in, info, handler) } var _Attacher_serviceDesc = grpc.ServiceDesc{ ServiceName: "s1mme.Attacher", HandlerType: (*AttacherServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "Attach", Handler: _Attacher_Attach_Handler, }, { MethodName: "Detach", Handler: _Attacher_Detach_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "s1mme.proto", } ================================================ FILE: examples/gw-tester/s1mme/s1mme.proto ================================================ syntax = "proto3"; package s1mme; // Attacher defines the service to attach UE. service Attacher { rpc Attach (AttachRequest) returns (AttachResponse) {} rpc Detach (DetachRequest) returns (DetachResponse) {} } // AttachRequest is used to request MME to create a session/bearer. message AttachRequest { string imsi = 1; string msisdn = 2; string imeisv = 3; string s1u_addr = 4; string src_ip = 5; uint32 i_tei = 6; Location location = 7; bool reattach = 8; } // AttachResponse is used to respond to AttachRequest. message AttachResponse { Cause cause = 1; // result string sgw_addr = 2; uint32 o_tei = 3; } // Cause represents a result of attach / detach. enum Cause { INVALID = 0; SUCCESS = 1; GW_UNAVAILABLE = 2; } // Location represents a set of location-related information. message Location { string mcc = 1; string mnc = 2; RATType rat_type = 3; uint32 tai = 4; uint32 eci = 5; enum RATType { INVALID = 0; UTRAN = 1; GERAN = 2; WLAN = 3; GAN = 4; HSPA_EVOLUTION = 5; EUTRAN = 6; VIRTUAL = 7; EUTRAN_NB_IOT = 8; LTEM = 9; NR = 10; } } // DetachRequest is used to request MME to delete a session/bearer. message DetachRequest { string imsi = 1; } // DetachResponse is used to respond to DetachRequest. message DetachResponse { Cause cause = 1; } ================================================ FILE: examples/gw-tester/sgw/config.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "io/ioutil" "gopkg.in/yaml.v2" ) // Config is a configurations loaded from yaml. type Config struct { LocalAddrs struct { S11IP string `yaml:"s11_ip"` S1UIP string `yaml:"s1u_ip"` S5CIP string `yaml:"s5c_ip"` S5UIP string `yaml:"s5u_ip"` } `yaml:"local_addresses"` UseKernelGTP bool `yaml:"use_kernel_gtp"` PromAddr string `yaml:"prom_addr"` } func loadConfig(path string) (*Config, error) { buf, err := ioutil.ReadFile(path) if err != nil { return nil, err } c := &Config{} if err := yaml.Unmarshal(buf, c); err != nil { return nil, err } return c, nil } ================================================ FILE: examples/gw-tester/sgw/main.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Command sgw is a dead simple implementation of S-GW. package main import ( "context" "flag" "log" "os" "os/signal" "syscall" ) func main() { var configPath = flag.String("config", "./sgw.yml", "Path to the configuration file.") flag.Parse() log.SetPrefix("[S-GW] ") cfg, err := loadConfig(*configPath) if err != nil { log.Println(err) return } sgw, err := newSGW(cfg) if err != nil { log.Printf("failed to initialize SGW: %s", err) } defer sgw.close() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGHUP) ctx, cancel := context.WithCancel(context.Background()) defer cancel() fatalCh := make(chan error) go func() { if err := sgw.run(ctx); err != nil { fatalCh <- err } }() for { select { case sig := <-sigCh: // TODO: reload config on receiving SIGHUP log.Println(sig) return case err := <-sgw.errCh: log.Printf("WARN: %s", err) case err := <-fatalCh: log.Printf("FATAL: %s", err) return } } } ================================================ FILE: examples/gw-tester/sgw/metrics.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/vishvananda/netlink" ) type metricsCollector struct { activeSessions prometheus.GaugeFunc activeBearers prometheus.GaugeFunc messagesSent *prometheus.CounterVec messagesReceived *prometheus.CounterVec } func (s *sgw) runMetricsCollector() error { mc := &metricsCollector{} mc.activeSessions = promauto.NewGaugeFunc( prometheus.GaugeOpts{ Name: "sgw_active_sessions", Help: "number of session established currently", }, func() float64 { return float64(s.s11Conn.SessionCount()) }, ) mc.activeBearers = promauto.NewGaugeFunc( prometheus.GaugeOpts{ Name: "sgw_active_bearers", Help: "number of GTP-U tunnels established currently", }, func() float64 { tunnels, err := netlink.GTPPDPList() if err != nil { log.Printf("metrics: could not get tunnels: %s", err) return 0 } return float64(len(tunnels)) }, ) mc.messagesSent = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "sgw_messages_sent_total", Help: "number of message sent by messagge type", }, []string{"dst", "type"}, ) mc.messagesReceived = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "sgw_messages_received_total", Help: "number of message received by messagge type", }, []string{"src", "type"}, ) s.mc = mc return nil } ================================================ FILE: examples/gw-tester/sgw/s11_handlers.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "fmt" "log" "net" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) func (s *sgw) handleCreateSessionRequest(s11Conn *gtpv2.Conn, mmeAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), mmeAddr) if s.mc != nil { s.mc.messagesReceived.WithLabelValues(mmeAddr.String(), msg.MessageTypeName()).Inc() } s11Session := gtpv2.NewSession(mmeAddr, >pv2.Subscriber{Location: >pv2.Location{}}) s11Bearer := s11Session.GetDefaultBearer() // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). csReqFromMME := msg.(*message.CreateSessionRequest) var pgwAddrString string if fteidcIE := csReqFromMME.PGWS5S8FTEIDC; fteidcIE != nil { ip, err := fteidcIE.IPAddress() if err != nil { return err } pgwAddrString = ip + gtpv2.GTPCPort teid, err := fteidcIE.TEID() if err != nil { return err } s11Session.AddTEID(gtpv2.IFTypeS5S8PGWGTPC, teid) } else { return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } if fteidcIE := csReqFromMME.SenderFTEIDC; fteidcIE != nil { teid, err := fteidcIE.TEID() if err != nil { return err } s11Session.AddTEID(gtpv2.IFTypeS11MMEGTPC, teid) } else { return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } raddr, err := net.ResolveUDPAddr("udp", pgwAddrString) if err != nil { return err } // keep session information retrieved from the message. // XXX - should return error if required IE is missing. if imsiIE := csReqFromMME.IMSI; imsiIE != nil { imsi, err := imsiIE.IMSI() if err != nil { return err } // remove previous session for the same subscriber if exists. sess, err := s11Conn.GetSessionByIMSI(imsi) if err != nil { switch err.(type) { case *gtpv2.UnknownIMSIError: // whole new session. just ignore. default: return fmt.Errorf("got something unexpected: %w", err) } } else { s11Conn.RemoveSession(sess) } s11Session.IMSI = imsi } else { return >pv2.RequiredIEMissingError{Type: ie.IMSI} } if msisdnIE := csReqFromMME.MSISDN; msisdnIE != nil { s11Session.MSISDN, err = msisdnIE.MSISDN() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.MSISDN} } if meiIE := csReqFromMME.MEI; meiIE != nil { s11Session.IMEI, err = meiIE.MobileEquipmentIdentity() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.MobileEquipmentIdentity} } if apnIE := csReqFromMME.APN; apnIE != nil { s11Bearer.APN, err = apnIE.AccessPointName() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.AccessPointName} } if netIE := csReqFromMME.ServingNetwork; netIE != nil { s11Session.MCC, err = netIE.MCC() if err != nil { return err } s11Session.MNC, err = netIE.MNC() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.ServingNetwork} } if ratIE := csReqFromMME.RATType; ratIE != nil { s11Session.RATType, err = ratIE.RATType() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.RATType} } s11sgwFTEID := s11Conn.NewSenderFTEID(s.s11IP, "") s11sgwTEID := s11sgwFTEID.MustTEID() s11Conn.RegisterSession(s11sgwTEID, s11Session) s5cFTEID := s.s5cConn.NewSenderFTEID(s.s5cIP, "") s5uFTEID := s.s5uConn.NewFTEID(gtpv2.IFTypeS5S8SGWGTPU, s.s5uIP, "").WithInstance(2) s5Session, seq, err := s.s5cConn.CreateSession( raddr, csReqFromMME.IMSI, csReqFromMME.MSISDN, csReqFromMME.MEI, csReqFromMME.ServingNetwork, csReqFromMME.RATType, csReqFromMME.IndicationFlags, s5cFTEID, csReqFromMME.PGWS5S8FTEIDC, csReqFromMME.APN, csReqFromMME.SelectionMode, csReqFromMME.PDNType, csReqFromMME.PAA, csReqFromMME.APNRestriction, csReqFromMME.AMBR, csReqFromMME.ULI, ie.NewBearerContext( ie.NewEPSBearerID(5), s5uFTEID, ie.NewBearerQoS(1, 2, 1, 0xff, 0, 0, 0, 0), ), csReqFromMME.MMEFQCSID, ie.NewFullyQualifiedCSID(s.s5uIP, 1).WithInstance(1), ) if err != nil { return err } s5Session.AddTEID(s5uFTEID.MustInterfaceType(), s5uFTEID.MustTEID()) log.Printf("Sent Create Session Request to %s for %s", pgwAddrString, s5Session.IMSI) if s.mc != nil { s.mc.messagesSent.WithLabelValues(mmeAddr.String(), "Create Session Request").Inc() } var csRspFromSGW *message.CreateSessionResponse s11mmeTEID, err := s11Session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { s11Conn.RemoveSession(s11Session) return err } incomingMsg, err := s11Session.WaitMessage(seq, 5*time.Second) if err != nil { csRspFromSGW = message.NewCreateSessionResponse( s11mmeTEID, 0, ie.NewCause(gtpv2.CauseNoResourcesAvailable, 0, 0, 0, nil), ) if err := s11Conn.RespondTo(mmeAddr, csReqFromMME, csRspFromSGW); err != nil { s11Conn.RemoveSession(s11Session) return err } log.Printf( "Sent %s with failure code: %d, target subscriber: %s", csRspFromSGW.MessageTypeName(), gtpv2.CausePGWNotResponding, s11Session.IMSI, ) s11Conn.RemoveSession(s11Session) return err } var csRspFromPGW *message.CreateSessionResponse switch m := incomingMsg.(type) { case *message.CreateSessionResponse: // move forward csRspFromPGW = m bearer := s11Session.GetDefaultBearer() if ie := csRspFromPGW.PAA; ie != nil { bearer.SubscriberIP, err = ie.IPAddress() if err != nil { return err } } default: s11Conn.RemoveSession(s11Session) return >pv2.UnexpectedTypeError{Msg: incomingMsg} } // if everything in CreateSessionResponse seems OK, relay it to MME. s1usgwFTEID := s.s1uConn.NewFTEID(gtpv2.IFTypeS1USGWGTPU, s.s1uIP, "") csRspFromSGW = csRspFromPGW csRspFromSGW.SenderFTEIDC = s11sgwFTEID csRspFromSGW.SGWFQCSID = ie.NewFullyQualifiedCSID(s.s1uIP, 1).WithInstance(1) csRspFromSGW.BearerContextsCreated[0].Add(s1usgwFTEID) csRspFromSGW.BearerContextsCreated[0].Remove(ie.ChargingID, 0) csRspFromSGW.SetTEID(s11mmeTEID) csRspFromSGW.SetLength() s11Session.AddTEID(s11sgwFTEID.MustInterfaceType(), s11sgwTEID) s11Session.AddTEID(s1usgwFTEID.MustInterfaceType(), s1usgwFTEID.MustTEID()) if err := s11Conn.RespondTo(mmeAddr, csReqFromMME, csRspFromSGW); err != nil { s11Conn.RemoveSession(s11Session) return err } if s.mc != nil { s.mc.messagesSent.WithLabelValues(mmeAddr.String(), csRspFromSGW.MessageTypeName()).Inc() } s5cpgwTEID, err := s5Session.GetTEID(gtpv2.IFTypeS5S8PGWGTPC) if err != nil { s11Conn.RemoveSession(s11Session) return err } s5csgwTEID, err := s5Session.GetTEID(gtpv2.IFTypeS5S8SGWGTPC) if err != nil { s11Conn.RemoveSession(s11Session) return err } if err := s11Session.Activate(); err != nil { s11Conn.RemoveSession(s11Session) return err } log.Printf( "Session created with MME and P-GW for Subscriber: %s;\n\tS11 MME: %s, TEID->: %#x, TEID<-: %#x\n\tS5C P-GW: %s, TEID->: %#x, TEID<-: %#x", s5Session.Subscriber.IMSI, mmeAddr, s11mmeTEID, s11sgwTEID, pgwAddrString, s5cpgwTEID, s5csgwTEID, ) return nil } func (s *sgw) handleModifyBearerRequest(s11Conn *gtpv2.Conn, mmeAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), mmeAddr) if s.mc != nil { s.mc.messagesReceived.WithLabelValues(mmeAddr.String(), msg.MessageTypeName()).Inc() } s11Session, err := s11Conn.GetSessionByTEID(msg.TEID(), mmeAddr) if err != nil { return err } s5cSession, err := s.s5cConn.GetSessionByIMSI(s11Session.IMSI) if err != nil { return err } s1uBearer := s11Session.GetDefaultBearer() s5uBearer := s5cSession.GetDefaultBearer() var enbIP string mbReqFromMME := msg.(*message.ModifyBearerRequest) if brCtxIE := mbReqFromMME.BearerContextsToBeModified; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.Indication: // do nothing in this implementation. case ie.FullyQualifiedTEID: if err := s.handleFTEIDU(childIE, s11Session, s1uBearer); err != nil { return err } enbIP, err = childIE.IPAddress() if err != nil { return err } } } } else { return >pv2.RequiredIEMissingError{Type: ie.BearerContext} } s11mmeTEID, err := s11Session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { return err } s1usgwTEID, err := s11Session.GetTEID(gtpv2.IFTypeS1USGWGTPU) if err != nil { return err } s5usgwTEID, err := s5cSession.GetTEID(gtpv2.IFTypeS5S8SGWGTPU) if err != nil { return err } pgwIP, _, err := net.SplitHostPort(s5uBearer.RemoteAddress().String()) if err != nil { return err } if s.useKernelGTP { if err := s.s1uConn.AddTunnelOverride( net.ParseIP(enbIP), net.ParseIP(s1uBearer.SubscriberIP), s1uBearer.OutgoingTEID(), s1usgwTEID, ); err != nil { return err } if err := s.s5uConn.AddTunnelOverride( net.ParseIP(pgwIP), net.ParseIP(s5uBearer.SubscriberIP), s5uBearer.OutgoingTEID(), s5usgwTEID, ); err != nil { return err } } else { if err := s.s1uConn.RelayTo( s.s5uConn, s1usgwTEID, s5uBearer.OutgoingTEID(), s5uBearer.RemoteAddress(), ); err != nil { return err } if err := s.s5uConn.RelayTo( s.s1uConn, s5usgwTEID, s1uBearer.OutgoingTEID(), s1uBearer.RemoteAddress(), ); err != nil { return err } } mbRspFromSGW := message.NewModifyBearerResponse( s11mmeTEID, 0, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(s1uBearer.EBI), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1USGWGTPU, s1usgwTEID, s.s1uIP, ""), ), ) if err := s11Conn.RespondTo(mmeAddr, msg, mbRspFromSGW); err != nil { return err } if s.mc != nil { s.mc.messagesSent.WithLabelValues(mmeAddr.String(), mbRspFromSGW.MessageTypeName()).Inc() } log.Printf( "Started listening on U-Plane for Subscriber: %s;\n\tS1-U: %s\n\tS5-U: %s", s11Session.IMSI, s.s1uConn.LocalAddr(), s.s5uConn.LocalAddr(), ) return nil } func (s *sgw) handleDeleteSessionRequest(s11Conn *gtpv2.Conn, mmeAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), mmeAddr) if s.mc != nil { s.mc.messagesReceived.WithLabelValues(mmeAddr.String(), msg.MessageTypeName()).Inc() } // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). dsReqFromMME := msg.(*message.DeleteSessionRequest) s11Session, err := s11Conn.GetSessionByTEID(msg.TEID(), mmeAddr) if err != nil { return err } s5Session, err := s.s5cConn.GetSessionByIMSI(s11Session.IMSI) if err != nil { return err } s5cpgwTEID, err := s5Session.GetTEID(gtpv2.IFTypeS5S8PGWGTPC) if err != nil { return err } seq, err := s.s5cConn.DeleteSession( s5cpgwTEID, s5Session, ie.NewEPSBearerID(s5Session.GetDefaultBearer().EBI), ) if err != nil { return err } var dsRspFromSGW *message.DeleteSessionResponse s11mmeTEID, err := s11Session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { return err } incomingMessage, err := s11Session.WaitMessage(seq, 5*time.Second) if err != nil { dsRspFromSGW = message.NewDeleteSessionResponse( s11mmeTEID, 0, ie.NewCause(gtpv2.CausePGWNotResponding, 0, 0, 0, nil), ) if err := s11Conn.RespondTo(mmeAddr, dsReqFromMME, dsRspFromSGW); err != nil { return err } log.Printf( "Sent %s with failure code: %d, target subscriber: %s", dsRspFromSGW.MessageTypeName(), gtpv2.CausePGWNotResponding, s11Session.IMSI, ) if s.mc != nil { s.mc.messagesSent.WithLabelValues(mmeAddr.String(), dsRspFromSGW.MessageTypeName()).Inc() } return err } // use the cause as it is. switch m := incomingMessage.(type) { case *message.DeleteSessionResponse: // move forward dsRspFromSGW = m default: return >pv2.UnexpectedTypeError{Msg: incomingMessage} } dsRspFromSGW.SetTEID(s11mmeTEID) if err := s11Conn.RespondTo(mmeAddr, msg, dsRspFromSGW); err != nil { return err } log.Printf("Session deleted for Subscriber: %s", s11Session.IMSI) if s.mc != nil { s.mc.messagesSent.WithLabelValues(mmeAddr.String(), dsRspFromSGW.MessageTypeName()).Inc() } s11Conn.RemoveSession(s11Session) return nil } func (s *sgw) handleDeleteBearerResponse(s11Conn *gtpv2.Conn, mmeAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), mmeAddr) if s.mc != nil { s.mc.messagesReceived.WithLabelValues(mmeAddr.String(), msg.MessageTypeName()).Inc() } s11Session, err := s11Conn.GetSessionByTEID(msg.TEID(), mmeAddr) if err != nil { return err } s5Session, err := s.s5cConn.GetSessionByIMSI(s11Session.IMSI) if err != nil { return err } if err := gtpv2.PassMessageTo(s5Session, msg, 5*time.Second); err != nil { return err } // remove bearer in handleDeleteBearerRequest instead of doing here, // as Delete Bearer Request does not necessarily have EBI. return nil } func (s *sgw) handleFTEIDU(fteiduIE *ie.IE, session *gtpv2.Session, bearer *gtpv2.Bearer) error { if fteiduIE.Type != ie.FullyQualifiedTEID { return >pv2.UnexpectedIEError{IEType: fteiduIE.Type} } ip, err := fteiduIE.IPAddress() if err != nil { return err } addr, err := net.ResolveUDPAddr("udp", ip+gtpv2.GTPUPort) if err != nil { return err } bearer.SetRemoteAddress(addr) teid, err := fteiduIE.TEID() if err != nil { return err } bearer.SetOutgoingTEID(teid) it, err := fteiduIE.InterfaceType() if err != nil { return err } session.AddTEID(it, teid) return nil } ================================================ FILE: examples/gw-tester/sgw/s5_handlers.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "fmt" "log" "net" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) func (s *sgw) handleCreateSessionResponse(s5cConn *gtpv2.Conn, pgwAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), pgwAddr) if s.mc != nil { s.mc.messagesReceived.WithLabelValues(pgwAddr.String(), msg.MessageTypeName()).Inc() } s5Session, err := s5cConn.GetSessionByTEID(msg.TEID(), pgwAddr) if err != nil { return err } // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). csRspFromPGW := msg.(*message.CreateSessionResponse) // check Cause value first. if causeIE := csRspFromPGW.Cause; causeIE != nil { cause, err := causeIE.Cause() if err != nil { return err } if cause != gtpv2.CauseRequestAccepted { s5cConn.RemoveSession(s5Session) // this is not such a fatal error worth stopping the whole program. // in the real case it is better to take some action based on the Cause, though. return >pv2.CauseNotOKError{ MsgType: csRspFromPGW.MessageTypeName(), Cause: cause, Msg: fmt.Sprintf("subscriber: %s", s5Session.IMSI), } } } else { s5cConn.RemoveSession(s5Session) return >pv2.RequiredIEMissingError{ Type: ie.Cause, } } bearer := s5Session.GetDefaultBearer() // retrieve values that P-GW gave. if paaIE := csRspFromPGW.PAA; paaIE != nil { ip, err := paaIE.IPAddress() if err != nil { return err } bearer.SubscriberIP = ip } else { s5cConn.RemoveSession(s5Session) return >pv2.RequiredIEMissingError{Type: ie.PDNAddressAllocation} } if fteidcIE := csRspFromPGW.PGWS5S8FTEIDC; fteidcIE != nil { it, err := fteidcIE.InterfaceType() if err != nil { return err } teid, err := fteidcIE.TEID() if err != nil { return err } s5Session.AddTEID(it, teid) } else { s5cConn.RemoveSession(s5Session) return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } if brCtxIE := csRspFromPGW.BearerContextsCreated; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.Cause: cause, err := childIE.Cause() if err != nil { return err } if cause != gtpv2.CauseRequestAccepted { s5cConn.RemoveSession(s5Session) return >pv2.CauseNotOKError{ MsgType: csRspFromPGW.MessageTypeName(), Cause: cause, Msg: fmt.Sprintf("subscriber: %s", s5Session.IMSI), } } case ie.EPSBearerID: ebi, err := childIE.EPSBearerID() if err != nil { return err } bearer.EBI = ebi case ie.FullyQualifiedTEID: if err := s.handleFTEIDU(childIE, s5Session, bearer); err != nil { return err } case ie.ChargingID: cid, err := childIE.ChargingID() if err != nil { return err } bearer.ChargingID = cid } } } else { s5cConn.RemoveSession(s5Session) return >pv2.RequiredIEMissingError{Type: ie.BearerContext} } if err := s5Session.Activate(); err != nil { s5cConn.RemoveSession(s5Session) return err } s11Session, err := s.s11Conn.GetSessionByIMSI(s5Session.IMSI) if err != nil { return err } if err := gtpv2.PassMessageTo(s11Session, csRspFromPGW, 5*time.Second); err != nil { return err } return nil } func (s *sgw) handleDeleteSessionResponse(s5cConn *gtpv2.Conn, pgwAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), pgwAddr) if s.mc != nil { s.mc.messagesReceived.WithLabelValues(pgwAddr.String(), msg.MessageTypeName()).Inc() } s5Session, err := s5cConn.GetSessionByTEID(msg.TEID(), pgwAddr) if err != nil { return err } s11Session, err := s.s11Conn.GetSessionByIMSI(s5Session.IMSI) if err != nil { return err } if err := gtpv2.PassMessageTo(s11Session, msg, 5*time.Second); err != nil { return err } // even the cause indicates failure, session should be removed locally. log.Printf("Session deleted for Subscriber: %s", s5Session.IMSI) s5cConn.RemoveSession(s5Session) return nil } func (s *sgw) handleDeleteBearerRequest(s5cConn *gtpv2.Conn, pgwAddr net.Addr, msg message.Message) error { log.Printf("Received %s from %s", msg.MessageTypeName(), pgwAddr) if s.mc != nil { s.mc.messagesReceived.WithLabelValues(pgwAddr.String(), msg.MessageTypeName()).Inc() } s5Session, err := s5cConn.GetSessionByTEID(msg.TEID(), pgwAddr) if err != nil { return err } s11Session, err := s.s11Conn.GetSessionByIMSI(s5Session.IMSI) if err != nil { return err } s5cpgwTEID, err := s5Session.GetTEID(gtpv2.IFTypeS5S8PGWGTPC) if err != nil { return err } s11mmeTEID, err := s11Session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { return err } // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). dbReqFromPGW := msg.(*message.DeleteBearerRequest) var dbRspFromSGW *message.DeleteBearerResponse var ebi *ie.IE if ie := dbReqFromPGW.LinkedEBI; ie != nil { ebi = ie } if e := dbReqFromPGW.EBIs; e != nil { ebiIE := e[0] // shouldn't be both. if ebi != nil { dbRspFromSGW = message.NewDeleteBearerResponse( s5cpgwTEID, 0, ie.NewCause(gtpv2.CauseContextNotFound, 0, 0, 0, ebiIE), ) if err := s5cConn.RespondTo(pgwAddr, dbReqFromPGW, dbRspFromSGW); err != nil { return err } return fmt.Errorf("%T from %s had both Linked EBI and EBIs IE", dbReqFromPGW, pgwAddr) } ebi = ebiIE } if ebi == nil { dbRspFromSGW = message.NewDeleteBearerResponse( s5cpgwTEID, 0, ie.NewCause(gtpv2.CauseMandatoryIEMissing, 0, 0, 0, ie.NewEPSBearerID(0), ), ) if err := s5cConn.RespondTo(pgwAddr, dbReqFromPGW, dbRspFromSGW); err != nil { return err } if s.mc != nil { s.mc.messagesSent.WithLabelValues(pgwAddr.String(), dbRspFromSGW.MessageTypeName()).Inc() } return err } // check if bearer associated with EBI exists or not. _, err = s5Session.LookupBearerByEBI(ebi.MustEPSBearerID()) if err != nil { dbRspFromSGW = message.NewDeleteBearerResponse( s5cpgwTEID, 0, ie.NewCause(gtpv2.CauseContextNotFound, 0, 0, 0, nil), ) if err := s5cConn.RespondTo(pgwAddr, dbReqFromPGW, dbRspFromSGW); err != nil { return err } if s.mc != nil { s.mc.messagesSent.WithLabelValues(pgwAddr.String(), dbRspFromSGW.MessageTypeName()).Inc() } return err } // forward to MME seq, err := s.s11Conn.DeleteBearer(s11mmeTEID, s11Session, ebi) if err != nil { return err } // wait for response from MME for 5 seconds incomingMessage, err := s5Session.WaitMessage(seq, 5*time.Second) if err != nil { dbRspFromSGW = message.NewDeleteBearerResponse( s5cpgwTEID, 0, ie.NewCause(gtpv2.CauseNoResourcesAvailable, 0, 0, 0, nil), ) if err := s5cConn.RespondTo(pgwAddr, dbReqFromPGW, dbRspFromSGW); err != nil { return err } if s.mc != nil { s.mc.messagesSent.WithLabelValues(pgwAddr.String(), dbRspFromSGW.MessageTypeName()).Inc() } // remove anyway, as P-GW no longer keeps bearer locally s5Session.RemoveBearerByEBI(ebi.MustEPSBearerID()) s11Session.RemoveBearerByEBI(ebi.MustEPSBearerID()) return err } switch m := incomingMessage.(type) { case *message.DeleteBearerResponse: // move forward dbRspFromSGW = m default: return >pv2.UnexpectedTypeError{Msg: incomingMessage} } dbRspFromSGW.SetTEID(s5cpgwTEID) if err := s5cConn.RespondTo(pgwAddr, msg, dbRspFromSGW); err != nil { return err } if s.mc != nil { s.mc.messagesSent.WithLabelValues(pgwAddr.String(), dbRspFromSGW.MessageTypeName()).Inc() } s5Session.RemoveBearerByEBI(ebi.MustEPSBearerID()) s11Session.RemoveBearerByEBI(ebi.MustEPSBearerID()) return nil } ================================================ FILE: examples/gw-tester/sgw/sgw.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "context" "fmt" "log" "net" "net/http" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/vishvananda/netlink" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/message" ) type sgw struct { // C-Plane s11Addr, s5cAddr net.Addr s11Conn, s5cConn *gtpv2.Conn // U-Plane s1uAddr, s5uAddr net.Addr s1uConn, s5uConn *gtpv1.UPlaneConn s11IP, s5cIP, s1uIP, s5uIP string useKernelGTP bool addedRoutes []*netlink.Route addedRules []*netlink.Rule promAddr string mc *metricsCollector errCh chan error } func newSGW(cfg *Config) (*sgw, error) { s := &sgw{ errCh: make(chan error, 1), } var err error s.s11Addr, err = net.ResolveUDPAddr("udp", cfg.LocalAddrs.S11IP+gtpv2.GTPCPort) if err != nil { return nil, err } s.s11IP, _, err = net.SplitHostPort(s.s11Addr.String()) if err != nil { return nil, err } s.s5cAddr, err = net.ResolveUDPAddr("udp", cfg.LocalAddrs.S5CIP+gtpv2.GTPCPort) if err != nil { return nil, err } s.s5cIP, _, err = net.SplitHostPort(s.s5cAddr.String()) if err != nil { return nil, err } s.s1uAddr, err = net.ResolveUDPAddr("udp", cfg.LocalAddrs.S1UIP+gtpv2.GTPUPort) if err != nil { return nil, err } s.s1uIP, _, err = net.SplitHostPort(s.s1uAddr.String()) if err != nil { return nil, err } s.s5uAddr, err = net.ResolveUDPAddr("udp", cfg.LocalAddrs.S5UIP+gtpv2.GTPUPort) if err != nil { return nil, err } s.s5uIP, _, err = net.SplitHostPort(s.s5uAddr.String()) if err != nil { return nil, err } s.useKernelGTP = cfg.UseKernelGTP if !s.useKernelGTP { log.Println("WARN: U-Plane performance would be significantly less without Kernel GTP") } if cfg.PromAddr != "" { // validate if the address is valid or not. if _, err = net.ResolveTCPAddr("tcp", cfg.PromAddr); err != nil { return nil, err } s.promAddr = cfg.PromAddr } return s, nil } func (s *sgw) run(ctx context.Context) error { s.s11Conn = gtpv2.NewConn(s.s11Addr, gtpv2.IFTypeS11S4SGWGTPC, 0) go func() { if err := s.s11Conn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() log.Printf("Started serving S11 on %s", s.s11Addr) s.s5cConn = gtpv2.NewConn(s.s5cAddr, gtpv2.IFTypeS5S8SGWGTPC, 0) go func() { if err := s.s5cConn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() log.Printf("Started serving S5-C on %s", s.s5cAddr) // register handlers for ALL the message you expect remote endpoint to send. s.s11Conn.AddHandlers(map[uint8]gtpv2.HandlerFunc{ message.MsgTypeCreateSessionRequest: s.handleCreateSessionRequest, message.MsgTypeModifyBearerRequest: s.handleModifyBearerRequest, message.MsgTypeDeleteSessionRequest: s.handleDeleteSessionRequest, message.MsgTypeDeleteBearerResponse: s.handleDeleteBearerResponse, }) s.s5cConn.AddHandlers(map[uint8]gtpv2.HandlerFunc{ message.MsgTypeCreateSessionResponse: s.handleCreateSessionResponse, message.MsgTypeDeleteSessionResponse: s.handleDeleteSessionResponse, message.MsgTypeDeleteBearerRequest: s.handleDeleteBearerRequest, }) s.s1uConn = gtpv1.NewUPlaneConn(s.s1uAddr) if s.useKernelGTP { if err := s.s1uConn.EnableKernelGTP("gtp-sgw-s1", gtpv1.RoleGGSN); err != nil { return err } } go func() { if err := s.s1uConn.ListenAndServe(ctx); err != nil { log.Println(err) return } log.Println("uConn.ListenAndServe exitted") }() log.Printf("Started serving S1-U on %s", s.s1uAddr) s.s5uConn = gtpv1.NewUPlaneConn(s.s5uAddr) if s.useKernelGTP { if err := s.s5uConn.EnableKernelGTP("gtp-sgw-s5", gtpv1.RoleSGSN); err != nil { return err } } go func() { if err := s.s5uConn.ListenAndServe(ctx); err != nil { log.Println(err) return } log.Println("uConn.ListenAndServe exitted") }() log.Printf("Started serving S5-U on %s", s.s5uAddr) if s.useKernelGTP { if err := s.addRoutes(); err != nil { return err } } // start serving Prometheus, if address is given if s.promAddr != "" { if err := s.runMetricsCollector(); err != nil { return err } http.Handle("/metrics", promhttp.Handler()) go func() { if err := http.ListenAndServe(s.promAddr, nil); err != nil { log.Println(err) } }() log.Printf("Started serving Prometheus on %s", s.promAddr) } for { select { case <-ctx.Done(): return nil case err := <-s.errCh: log.Printf("Warning: %+v", err) } } } func (s *sgw) close() error { var errs []error for _, r := range s.addedRoutes { if err := netlink.RouteDel(r); err != nil { errs = append(errs, err) } } for _, r := range s.addedRules { if err := netlink.RuleDel(r); err != nil { errs = append(errs, err) } } if s.s1uConn != nil { if err := s.s1uConn.Close(); err != nil { errs = append(errs, err) } } if s.s5uConn != nil { if err := s.s5uConn.Close(); err != nil { errs = append(errs, err) } } if s.s11Conn != nil { if err := s.s11Conn.Close(); err != nil { errs = append(errs, err) } } if s.s5cConn != nil { if err := s.s5cConn.Close(); err != nil { errs = append(errs, err) } } close(s.errCh) if len(errs) > 0 { return fmt.Errorf("errors while closing S-GW: %+v", errs) } return nil } func (s *sgw) addRoutes() error { defnet := &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)} s1route := &netlink.Route{ // ip route replace Dst: defnet, // default LinkIndex: s.s5uConn.KernelGTP.Link.Index, // dev gtp-s5 Scope: netlink.SCOPE_LINK, // scope link Protocol: 4, // proto static Priority: 1, // metric 1 Table: 2001, // table 2001 } if err := netlink.RouteReplace(s1route); err != nil { return err } s.addedRoutes = append(s.addedRoutes, s1route) s5route := &netlink.Route{ // ip route replace Dst: defnet, // default LinkIndex: s.s1uConn.KernelGTP.Link.Attrs().Index, // dev gtp-s1 Scope: netlink.SCOPE_LINK, // scope link Protocol: 4, // proto static Priority: 1, // metric 1 Table: 2005, // table 2005 } if err := netlink.RouteReplace(s5route); err != nil { return err } s.addedRoutes = append(s.addedRoutes, s1route) rules, err := netlink.RuleList(0) if err != nil { return err } var s1found, s5found bool for _, r := range rules { if s1found && s5found { break } if r.IifName == s.s1uConn.KernelGTP.Link.Name && r.Table == 2001 { s1found = true } if r.IifName == s.s5uConn.KernelGTP.Link.Name && r.Table == 2005 { s5found = true } } if !s1found { rule := netlink.NewRule() rule.IifName = s.s1uConn.KernelGTP.Link.Name rule.Table = 2001 if err := netlink.RuleAdd(rule); err != nil { return err } s.addedRules = append(s.addedRules, rule) } if !s5found { rule := netlink.NewRule() rule.IifName = s.s5uConn.KernelGTP.Link.Name rule.Table = 2005 if err := netlink.RuleAdd(rule); err != nil { return err } s.addedRules = append(s.addedRules, rule) } return nil } ================================================ FILE: examples/gw-tester/sgw/sgw.yml ================================================ local_addresses: s11_ip: "127.0.1.13" s1u_ip: "127.0.0.13" s5c_ip: "127.0.1.14" s5u_ip: "127.0.0.14" use_kernel_gtp: false prom_addr: "127.0.10.3:58080" ================================================ FILE: examples/mme/main.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Command mme is a reference implementation of MME with go-gtp. // // MME follows the steps below if there's no unexpected events in the middle. // Note that the S1 and DNS procedures is just mocked to make it work in // standalone manner. // // 1. Exchange Echo to S-GW address specified in command-line argument. // // 2. Start dispatching subscribers by sending Create Session Request to S-GW. // APN is handled with getPGWIP(), which is hard-coded. // // 3. Wait for Create Session Response coming from S-GW with Cause="request accepted". // // 4. Create mocked UE and eNB with the required values set as told by S-GW, start // listening on the interface specified with s1enb flag, and send Modify Bearer Request // to S-GW. // // 5. Wait for Modify Bearer Response coming from S-GW with Cause="request accepted". // // 6. Start sending payload(ICMP Echo Request) encapsulated with GTPv1-U Header, and printing // the payload of encapsulated packets received. package main import ( "context" "flag" "fmt" "log" "net" "strings" "sync" "time" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) // command-line flags. var ( s11mme = flag.String("s11mme", "127.0.0.111", "local IP on S11 interface.") s11sgw = flag.String("s11sgw", "127.0.0.112", "S-GW's IP on S11 interface.") s1enb = flag.String("s1enb", "127.0.0.1", "local IP on S1-U of pseudo eNB.") ) // variables globally shared. var ( attachCh = make(chan *gtpv2.Subscriber) createdCh = make(chan string) loggerCh = make(chan string) errCh = make(chan error) once = sync.Once{} delWG = sync.WaitGroup{} ) func main() { flag.Parse() log.SetPrefix("[MME] ") laddr, err := net.ResolveUDPAddr("udp", *s11mme+gtpv2.GTPCPort) if err != nil { log.Println(err) return } raddr, err := net.ResolveUDPAddr("udp", *s11sgw+gtpv2.GTPCPort) if err != nil { log.Println(err) return } ctx, cancel := context.WithCancel(context.Background()) defer cancel() // setup *Conn first to check if the remote endpoint is awaken. s11Conn, err := gtpv2.Dial(ctx, laddr, raddr, gtpv2.IFTypeS11MMEGTPC, 0) if err != nil { log.Println(err) return } defer s11Conn.Close() log.Printf("Connection established with %s", raddr.String()) // register handlers for ALL the message you expect remote endpoint to send. // by default, Echo and VersionNotsupported is handled without explicit declaration. s11Conn.AddHandlers(map[uint8]gtpv2.HandlerFunc{ message.MsgTypeCreateSessionResponse: handleCreateSessionResponse, message.MsgTypeModifyBearerResponse: handleModifyBearerResponse, message.MsgTypeDeleteSessionResponse: handleDeleteSessionResponse, }) // Listen on eNB S1-U interface. enbUPlaneAddr, err := net.ResolveUDPAddr("udp", *s1enb+gtpv2.GTPUPort) if err != nil { log.Println(err) return } uConn = gtpv1.NewUPlaneConn(enbUPlaneAddr) defer uConn.Close() go func() { if err = uConn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() log.Printf("Started listening on %s", enbUPlaneAddr) // here you should wait for UEs to come attaching to your network. // in this example, the following five subscribers are to be attached. // working as worker-dispatcher is preferable in the real case go dispatch([]*gtpv2.Subscriber{{ IMSI: "123451234567891", MSISDN: "8130900000001", IMEI: "123456780000011", Location: >pv2.Location{MCC: "123", MNC: "45", RATType: gtpv2.RATTypeEUTRAN, TAI: 0x0001, ECI: 0x00000101}, }, { IMSI: "123451234567892", MSISDN: "8130900000002", IMEI: "123456780000012", Location: >pv2.Location{MCC: "123", MNC: "45", RATType: gtpv2.RATTypeEUTRAN, TAI: 0x0002, ECI: 0x00000202}, }, { IMSI: "123451234567893", MSISDN: "8130900000003", IMEI: "123456780000013", Location: >pv2.Location{MCC: "123", MNC: "45", RATType: gtpv2.RATTypeEUTRAN, TAI: 0x0003, ECI: 0x00000303}, }, { IMSI: "123451234567894", MSISDN: "8130900000004", IMEI: "123456780000014", Location: >pv2.Location{MCC: "123", MNC: "45", RATType: gtpv2.RATTypeEUTRAN, TAI: 0x0004, ECI: 0x00000404}, }, { IMSI: "123451234567895", MSISDN: "8130900000005", IMEI: "123456780000015", Location: >pv2.Location{MCC: "123", MNC: "45", RATType: gtpv2.RATTypeEUTRAN, TAI: 0x0005, ECI: 0x00000505}, }}) bearer := gtpv2.NewBearer(5, "", >pv2.QoSProfile{ PL: 2, QCI: 255, MBRUL: 0xffffffff, MBRDL: 0xffffffff, GBRUL: 0xffffffff, GBRDL: 0xffffffff, }) for { select { // print logs coming from handlers working background case str := <-loggerCh: log.Println(str) // print errors coming from handlers working background // it's better to switch over the error to distinguish fatal ones to others. case err := <-errCh: log.Printf("Warning: %s", err) // handle attach requests case sub := <-attachCh: log.Printf("Started creating session for subscriber: %s", sub.IMSI) go func() { bearer.APN = "some-apn-1.example" if sub.TAI%2 == 0 { bearer.APN = "some-apn-2.example" } if err := handleAttach(raddr, s11Conn, sub, bearer); err != nil { errCh <- err return } }() case imsi := <-createdCh: go func() { sess, err := s11Conn.GetSessionByIMSI(imsi) if err != nil { errCh <- err return } enbIP := strings.Split(*s1enb, ":")[0] enbFTEID := uConn.NewFTEID(gtpv2.IFTypeS1UeNodeBGTPU, enbIP, "") teid, err := sess.GetTEID(gtpv2.IFTypeS11S4SGWGTPC) if err != nil { errCh <- err } if _, err := s11Conn.ModifyBearer( teid, sess, ie.NewIndicationFromOctets(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), ie.NewBearerContext(ie.NewEPSBearerID(sess.GetDefaultBearer().EBI), enbFTEID), ); err != nil { errCh <- err return } it, err := enbFTEID.InterfaceType() if err != nil { errCh <- err return } enbTEID, err := enbFTEID.TEID() if err != nil { errCh <- err return } sess.AddTEID(it, enbTEID) loggerCh <- fmt.Sprintf("Sent Modify Bearer Request for %s", imsi) }() // delete all the sessions after 30 seconds case <-time.After(30 * time.Second): for _, sess := range s11Conn.Sessions() { teid, err := sess.GetTEID(gtpv2.IFTypeS11S4SGWGTPC) if err != nil { errCh <- gtpv2.ErrTEIDNotFound return } if _, err := s11Conn.DeleteSession(teid, sess); err != nil { log.Printf("Warning: %s", err) } delWG.Add(1) log.Printf("Sent Delete Session Request for %s", sess.IMSI) } // invoke goroutine to let the logger work go func() { delWG.Wait() log.Fatal("Inactivity timer expired, exitting...") }() } } } ================================================ FILE: examples/mme/mme.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "fmt" "net" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) func handleCreateSessionResponse(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), sgwAddr) // find the session associated with TEID session, err := c.GetSessionByTEID(msg.TEID(), sgwAddr) if err != nil { c.RemoveSession(session) return err } bearer := session.GetDefaultBearer() // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). csRspFromSGW := msg.(*message.CreateSessionResponse) // check Cause value first. if ie := csRspFromSGW.Cause; ie != nil { cause, err := ie.Cause() if err != nil { return err } if cause != gtpv2.CauseRequestAccepted { c.RemoveSession(session) return >pv2.CauseNotOKError{ MsgType: csRspFromSGW.MessageTypeName(), Cause: cause, Msg: fmt.Sprintf("subscriber: %s", session.IMSI), } } } else { return >pv2.RequiredIEMissingError{Type: msg.MessageType()} } if ie := csRspFromSGW.PAA; ie != nil { bearer.SubscriberIP, err = ie.IPAddress() if err != nil { return err } } if senderIE := csRspFromSGW.SenderFTEIDC; senderIE != nil { teid, err := senderIE.TEID() if err != nil { return err } session.AddTEID(gtpv2.IFTypeS11S4SGWGTPC, teid) } else { return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } s11sgwTEID, err := session.GetTEID(gtpv2.IFTypeS11S4SGWGTPC) if err != nil { c.RemoveSession(session) return err } s11mmeTEID, err := session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { c.RemoveSession(session) return err } if brCtxIE := csRspFromSGW.BearerContextsCreated; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.EPSBearerID: bearer.EBI, err = childIE.EPSBearerID() if err != nil { return err } case ie.FullyQualifiedTEID: if childIE.Instance() != 0 { continue } it, err := childIE.InterfaceType() if err != nil { return err } teid, err := childIE.TEID() if err != nil { return err } session.AddTEID(it, teid) } } } else { return >pv2.RequiredIEMissingError{Type: ie.BearerContext} } if err := session.Activate(); err != nil { c.RemoveSession(session) return err } createdCh <- session.Subscriber.IMSI loggerCh <- fmt.Sprintf( "Session created with S-GW for Subscriber: %s;\n\tS11 S-GW: %s, TEID->: %#x, TEID<-: %#x", session.Subscriber.IMSI, sgwAddr, s11sgwTEID, s11mmeTEID, ) return nil } func handleModifyBearerResponse(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), sgwAddr) session, err := c.GetSessionByTEID(msg.TEID(), sgwAddr) if err != nil { return err } mbRspFromSGW := msg.(*message.ModifyBearerResponse) if causeIE := mbRspFromSGW.Cause; causeIE != nil { cause, err := causeIE.Cause() if err != nil { return err } if cause != gtpv2.CauseRequestAccepted { return >pv2.CauseNotOKError{ MsgType: msg.MessageTypeName(), Cause: cause, Msg: fmt.Sprintf("subscriber: %s", session.IMSI), } } } else { return >pv2.RequiredIEMissingError{Type: ie.Cause} } mock := &mockUEeNB{ subscriberIP: session.GetDefaultBearer().SubscriberIP, payload: payload, } if brCtxIE := mbRspFromSGW.BearerContextsModified; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.FullyQualifiedTEID: if childIE.Instance() != 0 { continue } it, err := childIE.InterfaceType() if err != nil { return err } teid, err := childIE.TEID() if err != nil { return err } session.AddTEID(it, teid) ip, err := childIE.IPAddress() if err != nil { return err } sgwUAddr, err := net.ResolveUDPAddr("udp", ip+gtpv2.GTPUPort) if err != nil { return err } mock.raddr = sgwUAddr mock.teidOut = teid } } } else { return >pv2.RequiredIEMissingError{Type: ie.BearerContext} } go mock.run(errCh) loggerCh <- fmt.Sprintf("Bearer modified with S-GW for Subscriber: %s", session.IMSI) return nil } func handleDeleteSessionResponse(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), sgwAddr) session, err := c.GetSessionByTEID(msg.TEID(), sgwAddr) if err != nil { return err } c.RemoveSession(session) delWG.Done() loggerCh <- fmt.Sprintf("Session deleted with S-GW for Subscriber: %s", session.IMSI) return nil } ================================================ FILE: examples/mme/mock.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "errors" "fmt" "net" "strings" "time" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" ) // getPGWIP is to get P-GW's IP address according to APN. // // DNS should be used in the real case, but here, to keep the example simple, // this function just returns IP address hard-coded in apnIPMap. func getPGWIP(apn string) (string, error) { apnIPMap := map[string]string{ "some-apn-1.example": "127.0.0.52", "some-apn-2.example": "127.0.0.53", } if ip, ok := apnIPMap[apn]; ok { return ip, nil } return "", fmt.Errorf("got unknown APN: %s", apn) } // dispatch sends subscribers to attachCh, which will be handled in handleAttach(). func dispatch(subs []*gtpv2.Subscriber) { for _, sub := range subs { // wait for 0-255ms before sending request (just for a little bit of reality) /* u8buf := make([]byte, 1) rand.Read(u8buf) time.Sleep(time.Duration(u8buf[0]) * time.Millisecond) */ time.Sleep(100 * time.Millisecond) attachCh <- sub } } // handleAttach is to start the session creation on S11. // in the real case this should be called after the procedure on S1AP/NAS has been done. func handleAttach(raddr net.Addr, c *gtpv2.Conn, sub *gtpv2.Subscriber, br *gtpv2.Bearer) error { // remove previous session for the same subscriber if exists. sess, err := c.GetSessionByIMSI(sub.IMSI) if err != nil { switch err.(type) { case *gtpv2.UnknownIMSIError: // whole new session. just ignore. default: return fmt.Errorf("got something unexpected: %w", err) } } else { teid, err := sess.GetTEID(gtpv2.IFTypeS11S4SGWGTPC) if err != nil { return gtpv2.ErrTEIDNotFound } // send Delete Session Request to cleanup sessions in S/P-GW. if _, err := c.DeleteSession(teid, sess); err != nil { return fmt.Errorf("got something unexpected: %w", err) } c.RemoveSession(sess) } pgwAddr, err := getPGWIP(br.APN) if err != nil { return err } var pci, pvi uint8 if br.PCI { pci = 1 } if br.PVI { pvi = 1 } localIP := strings.Split(c.LocalAddr().String(), ":")[0] _, _, err = c.CreateSession( raddr, ie.NewIMSI(sub.IMSI), ie.NewMSISDN(sub.MSISDN), ie.NewMobileEquipmentIdentity(sub.IMEI), ie.NewUserLocationInformationStruct( ie.NewCGI(sub.MCC, sub.MNC, sub.LAC, sub.CI), ie.NewSAI(sub.MCC, sub.MNC, sub.LAC, sub.SAI), ie.NewRAI(sub.MCC, sub.MNC, sub.LAC, sub.RAI), ie.NewTAI(sub.MCC, sub.MNC, sub.TAI), ie.NewECGI(sub.MCC, sub.MNC, sub.ECI), ie.NewLAI(sub.MCC, sub.MNC, sub.LAC), ie.NewMENBI(sub.MCC, sub.MNC, sub.MeNBI), ie.NewEMENBI(sub.MCC, sub.MNC, sub.EMeNBI), ), ie.NewRATType(sub.RATType), ie.NewIndicationFromOctets(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), c.NewSenderFTEID(localIP, ""), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, 0, pgwAddr, "").WithInstance(1), ie.NewAccessPointName(br.APN), ie.NewSelectionMode(gtpv2.SelectionModeMSorNetworkProvidedAPNSubscribedVerified), ie.NewPDNType(gtpv2.PDNTypeIPv4), ie.NewPDNAddressAllocation("0.0.0.0"), ie.NewAPNRestriction(gtpv2.APNRestrictionNoExistingContextsorRestriction), ie.NewAggregateMaximumBitRate(0, 0), ie.NewBearerContext( ie.NewEPSBearerID(br.EBI), ie.NewBearerQoS(pci, br.PL, pvi, br.QCI, br.MBRUL, br.MBRDL, br.GBRUL, br.GBRDL), ), ie.NewFullyQualifiedCSID(localIP, 1), ie.NewServingNetwork(sub.MCC, sub.MNC), ie.NewUETimeZone(9*time.Hour, 0), ) if err != nil { return err } return nil } var ( uConn *gtpv1.UPlaneConn payload = []byte{ // ICMP Echo to 8.8.8.8 over IP(src will be replaced), checksum is invalid. // IP 0x45, 0x00, 0x00, 0x54, 0x00, 0x01, 0x40, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x08, 0x08, 0x08, 0x08, // ICMP 0x08, 0x00, 0x93, 0x6a, 0x00, 0x01, 0x00, 0x01, 0xdf, 0xd5, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, } ) type mockUEeNB struct { raddr net.Addr subscriberIP string teidOut uint32 payload []byte } func (m mockUEeNB) run(errCh chan error) { go func(teid uint32, payload []byte, raddr net.Addr) { for { copy(payload[12:16], net.ParseIP(m.subscriberIP).To4()) if _, err := uConn.WriteToGTP(teid, m.payload, raddr); err != nil { errCh <- err return } time.Sleep(3 * time.Second) } }(m.teidOut, m.payload, m.raddr) go once.Do(func() { buf := make([]byte, 1500) for { if uConn == nil { errCh <- errors.New("uConn conn is not open") return } n, raddr, _, err := uConn.ReadFromGTP(buf) if err != nil { errCh <- err return } loggerCh <- fmt.Sprintf("Received from %s: %x", raddr, buf[:n]) } }) } ================================================ FILE: examples/pgw/main.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Command pgw is a dead simple implementation of P-GW only with GTP-related features. // // This example is not maintained well. Please see example/gw-tester/pgw for better implementation. // // P-GW follows the steps below if there's no unexpected events in the middle. Note // that the Gx procedure is just mocked to make it work in standalone manner. // // 1. Wait for Create Session Request from S-GW. // // 2. Send Create Session Response to S-GW if the required IEs are not missing, and // start listening on the interface specified with s5u flag. // // 3. If Modify Bearer Request comes from S-GW, update bearer information. // // 4. If T-PDU comes from S-GW, print the payload of encapsulated packets received, // and respond to it with payload(ICMP Echo Reply). package main import ( "context" "flag" "log" "net" "time" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/message" ) // command-line arguments var ( s5c = flag.String("s5c", "127.0.0.52", "IP for S5-C interface.") s5u = flag.String("s5u", "127.0.0.4", "IP for S5-U interface.") ) func main() { flag.Parse() log.SetPrefix("[P-GW] ") s5cAddr, err := net.ResolveUDPAddr("udp", *s5c+gtpv2.GTPCPort) if err != nil { log.Println(err) return } ctx, cancel := context.WithCancel(context.Background()) defer cancel() // start listening on the specified IP:Port. s5cConn := gtpv2.NewConn(s5cAddr, gtpv2.IFTypeS5S8PGWGTPC, 0) go func() { if err := s5cConn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() log.Printf("Started serving C-Plane on %s", s5cAddr) // register handlers for ALL the message you expect remote endpoint to send. s5cConn.AddHandlers(map[uint8]gtpv2.HandlerFunc{ message.MsgTypeCreateSessionRequest: handleCreateSessionRequest, message.MsgTypeDeleteSessionRequest: handleDeleteSessionRequest, }) s5uAddr, err := net.ResolveUDPAddr("udp", *s5u+gtpv2.GTPUPort) if err != nil { log.Println(err) return } uConn = gtpv1.NewUPlaneConn(s5uAddr) defer uConn.Close() go func() { if err = uConn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() log.Printf("Started serving U-Plane on %s", s5uAddr) for { select { case str := <-loggerCh: log.Printf("%s", str) case err := <-errCh: log.Printf("Warning: %s", err) case <-time.After(10 * time.Second): var activeIMSIs []string for _, sess := range s5cConn.Sessions() { if !sess.IsActive() { continue } activeIMSIs = append(activeIMSIs, sess.IMSI) } if len(activeIMSIs) == 0 { continue } log.Println("Active Subscribers:") for _, imsi := range activeIMSIs { log.Printf("\t%s", imsi) } activeIMSIs = nil } } } ================================================ FILE: examples/pgw/pgw.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package main import ( "fmt" "net" "strings" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) // getSubscriberIP is to get IP address to be assigned to the subscriber. // // In the real case, P-GW may ask AAA and PCRF retrieve required information for subscriber, // but here, to keep the example simple, this just returns subscriber's IP address defined in // the map "subIPMap". func getSubscriberIP(sub *gtpv2.Subscriber) (string, error) { subIPMap := map[string]string{ "123451234567891": "10.10.10.1", "123451234567892": "10.10.10.2", "123451234567893": "10.10.10.3", "123451234567894": "10.10.10.4", "123451234567895": "10.10.10.5", } if ip, ok := subIPMap[sub.IMSI]; ok { return ip, nil } return "", fmt.Errorf("subscriber %s not found", sub.IMSI) } var ( loggerCh = make(chan string) errCh = make(chan error) uConn *gtpv1.UPlaneConn ) func handleCreateSessionRequest(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), sgwAddr) // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). csReqFromSGW := msg.(*message.CreateSessionRequest) // keep session information retrieved from the message. session := gtpv2.NewSession(sgwAddr, >pv2.Subscriber{Location: >pv2.Location{}}) bearer := session.GetDefaultBearer() var err error if imsiIE := csReqFromSGW.IMSI; imsiIE != nil { imsi, err := imsiIE.IMSI() if err != nil { return err } session.IMSI = imsi // remove previous session for the same subscriber if exists. sess, err := c.GetSessionByIMSI(imsi) if err != nil { switch err.(type) { case *gtpv2.UnknownIMSIError: // whole new session. just ignore. default: return fmt.Errorf("got something unexpected: %w", err) } } else { c.RemoveSession(sess) } } else { return >pv2.RequiredIEMissingError{Type: ie.IMSI} } if msisdnIE := csReqFromSGW.MSISDN; msisdnIE != nil { session.MSISDN, err = msisdnIE.MSISDN() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.MSISDN} } if meiIE := csReqFromSGW.MEI; meiIE != nil { session.IMEI, err = meiIE.MobileEquipmentIdentity() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.MobileEquipmentIdentity} } if apnIE := csReqFromSGW.APN; apnIE != nil { bearer.APN, err = apnIE.AccessPointName() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.AccessPointName} } if netIE := csReqFromSGW.ServingNetwork; netIE != nil { session.MNC, err = netIE.MNC() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.ServingNetwork} } if ratIE := csReqFromSGW.RATType; ratIE != nil { session.RATType, err = ratIE.RATType() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.RATType} } if fteidcIE := csReqFromSGW.SenderFTEIDC; fteidcIE != nil { teid, err := fteidcIE.TEID() if err != nil { return err } session.AddTEID(gtpv2.IFTypeS5S8SGWGTPC, teid) } else { return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } var teidOut uint32 if brCtxIE := csReqFromSGW.BearerContextsToBeCreated; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.EPSBearerID: bearer.EBI, err = childIE.EPSBearerID() if err != nil { return err } case ie.FullyQualifiedTEID: it, err := childIE.InterfaceType() if err != nil { return err } teidOut, err := childIE.TEID() if err != nil { return err } session.AddTEID(it, teidOut) } } } else { return >pv2.RequiredIEMissingError{Type: ie.BearerContext} } bearer.SubscriberIP, err = getSubscriberIP(session.Subscriber) if err != nil { return err } cIP := strings.Split(c.LocalAddr().String(), ":")[0] uIP := strings.Split(*s5u, ":")[0] s5cFTEID := c.NewSenderFTEID(cIP, "").WithInstance(1) s5uFTEID := uConn.NewFTEID(gtpv2.IFTypeS5S8PGWGTPU, uIP, "").WithInstance(2) s5sgwTEID, err := session.GetTEID(gtpv2.IFTypeS5S8SGWGTPC) if err != nil { return err } csRspFromPGW := message.NewCreateSessionResponse( s5sgwTEID, 0, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), s5cFTEID, ie.NewPDNAddressAllocation(bearer.SubscriberIP), ie.NewAPNRestriction(gtpv2.APNRestrictionPublic2), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(bearer.EBI), s5uFTEID, ie.NewChargingID(bearer.ChargingID), ), ) if csReqFromSGW.SGWFQCSID != nil { csRspFromPGW.PGWFQCSID = ie.NewFullyQualifiedCSID(cIP, 1) } session.AddTEID(gtpv2.IFTypeS5S8PGWGTPC, s5cFTEID.MustTEID()) session.AddTEID(gtpv2.IFTypeS5S8PGWGTPU, s5uFTEID.MustTEID()) if err := c.RespondTo(sgwAddr, csReqFromSGW, csRspFromPGW); err != nil { return err } s5pgwTEID, err := session.GetTEID(gtpv2.IFTypeS5S8PGWGTPC) if err != nil { return err } c.RegisterSession(s5pgwTEID, session) // don't forget to activate and add session created to the session list if err := session.Activate(); err != nil { return err } go func() { buf := make([]byte, 1500) for { n, raddr, _, err := uConn.ReadFromGTP(buf) if err != nil { return } rsp := make([]byte, n) // update message type and checksum copy(rsp, buf[:n]) rsp[20] = 0 rsp[22] = 0x9b // swap IP copy(rsp[12:16], buf[16:20]) copy(rsp[16:20], buf[12:16]) if _, err := uConn.WriteToGTP(teidOut, rsp, raddr); err != nil { return } } }() loggerCh <- fmt.Sprintf("Session created with S-GW for subscriber: %s;\n\tS5C S-GW: %s, TEID->: %#x, TEID<-: %#x", session.Subscriber.IMSI, sgwAddr, s5sgwTEID, s5pgwTEID, ) return nil } func handleDeleteSessionRequest(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), sgwAddr) // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). session, err := c.GetSessionByTEID(msg.TEID(), sgwAddr) if err != nil { dsr := message.NewDeleteSessionResponse( 0, 0, ie.NewCause(gtpv2.CauseIMSIIMEINotKnown, 0, 0, 0, nil), ) if err := c.RespondTo(sgwAddr, msg, dsr); err != nil { return err } return err } // respond to S-GW with DeleteSessionResponse. teid, err := session.GetTEID(gtpv2.IFTypeS5S8SGWGTPC) if err != nil { loggerCh <- fmt.Sprintf("Error: %v", err) return nil } dsr := message.NewDeleteSessionResponse( teid, 0, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ) if err := c.RespondTo(sgwAddr, msg, dsr); err != nil { return err } loggerCh <- fmt.Sprintf("Session deleted for Subscriber: %s", session.IMSI) c.RemoveSession(session) return nil } ================================================ FILE: examples/sgw/main.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Command sgw is a dead simple implementation of S-GW only with GTP-related features. // // S-GW follows the steps below if there's no unexpected events in the middle. // // 1. Start listening on S11 interface. // // 2. If MME connects to S-GW with Create Session Request, S-GW sends Create Session Request // to P-GW whose IP is specified by MME with F-TEID IE. // // 3. Wait for Create Session Response coming from P-GW with Cause="request accepted", and // other IEs required are properly set. // // 4. Respond to MME with Create Session Response. Here the C-Plane Session is considered to // be created properly. // // 5. If MME sends Modify Bearer Request with eNB information inside, set incoming TEID to // Bearer and start listening on U-Plane. // // 6. If some U-Plane message comes from eNB/P-GW, relay it to P-GW/eNB with TEID and IP // properly set as told while exchanging the C-Plane signals. package main import ( "context" "flag" "log" "net" "time" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/message" ) // command-line arguments and global variables var ( s11 = flag.String("s11", "127.0.0.112", "local IP on S11 interface.") s5c = flag.String("s5c", "127.0.0.51", "local IP on S5-C interface.") s1u = flag.String("s1u", "127.0.0.2", "local IP on S1-U interface.") s5u = flag.String("s5u", "127.0.0.3", "local IP on S5-U interface.") sgw *sGateway ) type sGateway struct { s11Conn, s5cConn *gtpv2.Conn s1uConn, s5uConn *gtpv1.UPlaneConn loggerCh chan string errCh chan error } func newSGW(s11, s5c, s1u, s5u net.Addr) (*sGateway, error) { s := &sGateway{ loggerCh: make(chan string), errCh: make(chan error), } ctx := context.Background() var err error s.s11Conn = gtpv2.NewConn(s11, gtpv2.IFTypeS11S4SGWGTPC, 0) go func() { if err = s.s11Conn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() log.Printf("Started serving on %s", s11) s.s5cConn = gtpv2.NewConn(s5c, gtpv2.IFTypeS5S8SGWGTPC, 0) go func() { if err = s.s5cConn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() log.Printf("Started serving on %s", s5c) s.s1uConn = gtpv1.NewUPlaneConn(s1u) go func() { if err = s.s1uConn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() go func() { s.s5uConn = gtpv1.NewUPlaneConn(s5u) if err = s.s5uConn.ListenAndServe(ctx); err != nil { log.Println(err) return } }() return s, nil } func (s *sGateway) run() error { defer func() { s.s11Conn.Close() s.s5cConn.Close() }() // wait for events(logs, errors, timers). for { select { case str := <-s.loggerCh: log.Println(str) case err := <-s.errCh: log.Printf("Warning: %v", err) case <-time.After(10 * time.Second): var activeIMSIs []string for _, sess := range s.s11Conn.Sessions() { if !sess.IsActive() { continue } activeIMSIs = append(activeIMSIs, sess.IMSI) } if len(activeIMSIs) == 0 { continue } log.Println("Active Subscribers:") for _, imsi := range activeIMSIs { log.Printf("\t%s", imsi) } activeIMSIs = nil } } } func main() { flag.Parse() log.SetPrefix("[S-GW] ") // resolve specified IP:Port as net.UDPAddr. s11, err := net.ResolveUDPAddr("udp", *s11+gtpv2.GTPCPort) if err != nil { log.Println(err) return } s5c, err := net.ResolveUDPAddr("udp", *s5c+gtpv2.GTPCPort) if err != nil { log.Println(err) return } s1u, err := net.ResolveUDPAddr("udp", *s1u+gtpv2.GTPUPort) if err != nil { log.Println(err) return } s5u, err := net.ResolveUDPAddr("udp", *s5u+gtpv2.GTPUPort) if err != nil { log.Println(err) return } sgw, err = newSGW(s11, s5c, s1u, s5u) if err != nil { log.Println(err) return } // register handlers for ALL the message you expect remote endpoint to send. sgw.s11Conn.AddHandlers(map[uint8]gtpv2.HandlerFunc{ message.MsgTypeCreateSessionRequest: handleCreateSessionRequest, message.MsgTypeModifyBearerRequest: handleModifyBearerRequest, message.MsgTypeDeleteSessionRequest: handleDeleteSessionRequest, message.MsgTypeDeleteBearerResponse: handleDeleteBearerResponse, }) sgw.s5cConn.AddHandlers(map[uint8]gtpv2.HandlerFunc{ message.MsgTypeCreateSessionResponse: handleCreateSessionResponse, message.MsgTypeDeleteSessionResponse: handleDeleteSessionResponse, message.MsgTypeDeleteBearerRequest: handleDeleteBearerRequest, }) log.Fatal(sgw.run()) } ================================================ FILE: examples/sgw/s11.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Command sgw is a dead simple implementation of S-GW only with GTP-related features. package main import ( "fmt" "net" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) func handleCreateSessionRequest(s11Conn *gtpv2.Conn, mmeAddr net.Addr, msg message.Message) error { sgw.loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), mmeAddr) s11Session := gtpv2.NewSession(mmeAddr, >pv2.Subscriber{Location: >pv2.Location{}}) s11Bearer := s11Session.GetDefaultBearer() // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). csReqFromMME := msg.(*message.CreateSessionRequest) var pgwAddrString string if teidIE := csReqFromMME.PGWS5S8FTEIDC; teidIE != nil { ip, err := teidIE.IPAddress() if err != nil { return err } pgwAddrString = ip + gtpv2.GTPCPort teid, err := teidIE.TEID() if err != nil { return err } s11Session.AddTEID(gtpv2.IFTypeS5S8PGWGTPC, teid) } else { return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } if senderIE := csReqFromMME.SenderFTEIDC; senderIE != nil { teid, err := senderIE.TEID() if err != nil { return err } s11Session.AddTEID(gtpv2.IFTypeS11MMEGTPC, teid) } else { return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } laddr, err := net.ResolveUDPAddr("udp", *s5c+gtpv2.GTPCPort) if err != nil { return err } raddr, err := net.ResolveUDPAddr("udp", pgwAddrString) if err != nil { return err } // keep session information retrieved from the message. // XXX - should return error if required IE is missing. if imsiIE := csReqFromMME.IMSI; imsiIE != nil { imsi, err := imsiIE.IMSI() if err != nil { return err } // remove previous session for the same subscriber if exists. sess, err := s11Conn.GetSessionByIMSI(imsi) if err != nil { switch err.(type) { case *gtpv2.UnknownIMSIError: // whole new session. just ignore. default: return fmt.Errorf("got something unexpected: %w", err) } } else { s11Conn.RemoveSession(sess) } s11Session.IMSI = imsi } else { return >pv2.RequiredIEMissingError{Type: ie.IMSI} } if msisdnIE := csReqFromMME.MSISDN; msisdnIE != nil { s11Session.MSISDN, err = msisdnIE.MSISDN() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.MSISDN} } if meiIE := csReqFromMME.MEI; meiIE != nil { s11Session.IMEI, err = meiIE.MobileEquipmentIdentity() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.MobileEquipmentIdentity} } if apnIE := csReqFromMME.APN; apnIE != nil { s11Bearer.APN, err = apnIE.AccessPointName() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.AccessPointName} } if netIE := csReqFromMME.ServingNetwork; netIE != nil { s11Session.MCC, err = netIE.MCC() if err != nil { return err } s11Session.MNC, err = netIE.MNC() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.ServingNetwork} } if ratIE := csReqFromMME.RATType; ratIE != nil { s11Session.RATType, err = ratIE.RATType() if err != nil { return err } } else { return >pv2.RequiredIEMissingError{Type: ie.RATType} } s11IP, _, err := net.SplitHostPort(*s11 + gtpv2.GTPCPort) if err != nil { return fmt.Errorf("failed to get IP for S11: %w", err) } senderFTEID := s11Conn.NewSenderFTEID(s11IP, "") s11sgwTEID := senderFTEID.MustTEID() s11Conn.RegisterSession(s11sgwTEID, s11Session) s5cIP := laddr.IP.String() s5uIP, _, err := net.SplitHostPort(*s5u + gtpv2.GTPCPort) if err != nil { return err } s5cFTEID := sgw.s5cConn.NewSenderFTEID(s5cIP, "") s5uFTEID := sgw.s5uConn.NewFTEID(gtpv2.IFTypeS5S8SGWGTPU, s5uIP, "").WithInstance(2) s5Session, seq, err := sgw.s5cConn.CreateSession( raddr, csReqFromMME.IMSI, csReqFromMME.MSISDN, csReqFromMME.MEI, csReqFromMME.ServingNetwork, csReqFromMME.RATType, csReqFromMME.IndicationFlags, s5cFTEID, csReqFromMME.PGWS5S8FTEIDC, csReqFromMME.APN, csReqFromMME.SelectionMode, csReqFromMME.PDNType, csReqFromMME.PAA, csReqFromMME.APNRestriction, csReqFromMME.AMBR, csReqFromMME.ULI, ie.NewBearerContext( ie.NewEPSBearerID(5), s5uFTEID, ie.NewBearerQoS(1, 2, 1, 0xff, 0, 0, 0, 0), ), csReqFromMME.MMEFQCSID, ie.NewFullyQualifiedCSID(s5uIP, 1).WithInstance(1), ) if err != nil { return err } s5Session.AddTEID(s5uFTEID.MustInterfaceType(), s5uFTEID.MustTEID()) sgw.s5cConn.RegisterSession(s5cFTEID.MustTEID(), s5Session) sgw.loggerCh <- fmt.Sprintf("Sent Create Session Request to %s for %s", pgwAddrString, s5Session.IMSI) var csRspFromSGW *message.CreateSessionResponse s11mmeTEID, err := s11Session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { s11Conn.RemoveSession(s11Session) return err } incomingMsg, err := s11Session.WaitMessage(seq, 5*time.Second) if err != nil { csRspFromSGW = message.NewCreateSessionResponse( s11mmeTEID, 0, ie.NewCause(gtpv2.CauseNoResourcesAvailable, 0, 0, 0, nil), ) if err := s11Conn.RespondTo(mmeAddr, csReqFromMME, csRspFromSGW); err != nil { s11Conn.RemoveSession(s11Session) return err } sgw.loggerCh <- fmt.Sprintf( "Sent %s with failure code: %d, target subscriber: %s", csRspFromSGW.MessageTypeName(), gtpv2.CausePGWNotResponding, s11Session.IMSI, ) s11Conn.RemoveSession(s11Session) return err } var csRspFromPGW *message.CreateSessionResponse switch m := incomingMsg.(type) { case *message.CreateSessionResponse: // move forward csRspFromPGW = m default: s11Conn.RemoveSession(s11Session) return >pv2.UnexpectedTypeError{Msg: incomingMsg} } // if everything in CreateSessionResponse seems OK, relay it to MME. s1uIP, _, err := net.SplitHostPort(*s1u + gtpv2.GTPCPort) if err != nil { return fmt.Errorf("failed to get IP for S1-U: %w", err) } s1usgwFTEID := sgw.s1uConn.NewFTEID(gtpv2.IFTypeS1USGWGTPU, s1uIP, "") csRspFromSGW = csRspFromPGW csRspFromSGW.SenderFTEIDC = senderFTEID csRspFromSGW.SGWFQCSID = ie.NewFullyQualifiedCSID(s1uIP, 1).WithInstance(1) csRspFromSGW.BearerContextsCreated[0].Add(s1usgwFTEID) csRspFromSGW.BearerContextsCreated[0].Remove(ie.ChargingID, 0) csRspFromSGW.SetTEID(s11mmeTEID) csRspFromSGW.SetLength() if err := s11Conn.RespondTo(mmeAddr, csReqFromMME, csRspFromSGW); err != nil { s11Conn.RemoveSession(s11Session) return err } s11Session.AddTEID(senderFTEID.MustInterfaceType(), s11sgwTEID) s11Session.AddTEID(s1usgwFTEID.MustInterfaceType(), s1usgwFTEID.MustTEID()) s5cpgwTEID, err := s5Session.GetTEID(gtpv2.IFTypeS5S8PGWGTPC) if err != nil { s11Conn.RemoveSession(s11Session) return err } s5csgwTEID, err := s5Session.GetTEID(gtpv2.IFTypeS5S8SGWGTPC) if err != nil { s11Conn.RemoveSession(s11Session) return err } if err := s11Session.Activate(); err != nil { sgw.loggerCh <- fmt.Sprintf("Error: %v", err) s11Conn.RemoveSession(s11Session) return err } sgw.loggerCh <- fmt.Sprintf( "Session created with MME and P-GW for Subscriber: %s;\n\tS11 MME: %s, TEID->: %#x, TEID<-: %#x\n\tS5C P-GW: %s, TEID->: %#x, TEID<-: %#x", s5Session.Subscriber.IMSI, mmeAddr, s11mmeTEID, s11sgwTEID, pgwAddrString, s5cpgwTEID, s5csgwTEID, ) return nil } func handleModifyBearerRequest(s11Conn *gtpv2.Conn, mmeAddr net.Addr, msg message.Message) error { sgw.loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), mmeAddr) s11Session, err := s11Conn.GetSessionByTEID(msg.TEID(), mmeAddr) if err != nil { return err } s5cSession, err := sgw.s5cConn.GetSessionByIMSI(s11Session.IMSI) if err != nil { return err } s1uBearer := s11Session.GetDefaultBearer() s5uBearer := s5cSession.GetDefaultBearer() // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). mbReqFromMME := msg.(*message.ModifyBearerRequest) if brCtxIE := mbReqFromMME.BearerContextsToBeModified; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.Indication: // do nothing in this example. // S-GW should change its beahavior based on indication flags like; // - pass Modify Bearer Request to P-GW if handover is indicated. // - XXX... case ie.FullyQualifiedTEID: if err := handleFTEIDU(childIE, s11Session, s1uBearer); err != nil { return err } } } } s11mmeTEID, err := s11Session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { return err } s1usgwTEID, err := s11Session.GetTEID(gtpv2.IFTypeS1USGWGTPU) if err != nil { return err } s5usgwTEID, err := s5cSession.GetTEID(gtpv2.IFTypeS5S8SGWGTPU) if err != nil { return err } if err := sgw.s1uConn.RelayTo( sgw.s5uConn, s1usgwTEID, s5uBearer.OutgoingTEID(), s5uBearer.RemoteAddress(), ); err != nil { return err } if err := sgw.s5uConn.RelayTo( sgw.s1uConn, s5usgwTEID, s1uBearer.OutgoingTEID(), s1uBearer.RemoteAddress(), ); err != nil { return err } s1uIP, _, err := net.SplitHostPort(*s1u + gtpv2.GTPCPort) if err != nil { return err } mbRspFromSGW := message.NewModifyBearerResponse( s11mmeTEID, 0, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(s1uBearer.EBI), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1USGWGTPU, s1usgwTEID, s1uIP, ""), ), ) if err := s11Conn.RespondTo(mmeAddr, msg, mbRspFromSGW); err != nil { return err } sgw.loggerCh <- fmt.Sprintf( "Started listening on U-Plane for Subscriber: %s;\n\tS1-U: %s\n\tS5-U: %s", s11Session.IMSI, *s1u, *s5u, ) return nil } func handleDeleteSessionRequest(s11Conn *gtpv2.Conn, mmeAddr net.Addr, msg message.Message) error { sgw.loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), mmeAddr) // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). dsReqFromMME := msg.(*message.DeleteSessionRequest) s11Session, err := s11Conn.GetSessionByTEID(msg.TEID(), mmeAddr) if err != nil { return err } s5Session, err := sgw.s5cConn.GetSessionByIMSI(s11Session.IMSI) if err != nil { return err } s5cpgwTEID, err := s5Session.GetTEID(gtpv2.IFTypeS5S8PGWGTPC) if err != nil { return err } seq, err := sgw.s5cConn.DeleteSession( s5cpgwTEID, s5Session, ie.NewEPSBearerID(s5Session.GetDefaultBearer().EBI), ) if err != nil { return err } var dsRspFromSGW *message.DeleteSessionResponse s11mmeTEID, err := s11Session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { return err } incomingMsg, err := s11Session.WaitMessage(seq, 5*time.Second) if err != nil { dsRspFromSGW = message.NewDeleteSessionResponse( s11mmeTEID, 0, ie.NewCause(gtpv2.CausePGWNotResponding, 0, 0, 0, nil), ) if err := s11Conn.RespondTo(mmeAddr, dsReqFromMME, dsRspFromSGW); err != nil { return err } sgw.loggerCh <- fmt.Sprintf( "Sent %s with failure code: %d, target subscriber: %s", dsRspFromSGW.MessageTypeName(), gtpv2.CausePGWNotResponding, s11Session.IMSI, ) return err } // use the cause as it is. switch m := incomingMsg.(type) { case *message.DeleteSessionResponse: // move forward dsRspFromSGW = m default: return >pv2.UnexpectedTypeError{Msg: incomingMsg} } dsRspFromSGW.SetTEID(s11mmeTEID) if err := s11Conn.RespondTo(mmeAddr, msg, dsRspFromSGW); err != nil { return err } sgw.loggerCh <- fmt.Sprintf("Session deleted for Subscriber: %s", s11Session.IMSI) s11Conn.RemoveSession(s11Session) return nil } func handleDeleteBearerResponse(s11Conn *gtpv2.Conn, mmeAddr net.Addr, msg message.Message) error { sgw.loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), mmeAddr) s11Session, err := s11Conn.GetSessionByTEID(msg.TEID(), mmeAddr) if err != nil { return err } s5Session, err := sgw.s5cConn.GetSessionByIMSI(s11Session.IMSI) if err != nil { return err } if err := gtpv2.PassMessageTo(s5Session, msg, 5*time.Second); err != nil { return err } // remove bearer in handleDeleteBearerRequest instead of doing here, // as Delete Bearer Request does not necessarily have EBI. return nil } func handleFTEIDU(fteiduIE *ie.IE, session *gtpv2.Session, bearer *gtpv2.Bearer) error { if fteiduIE.Type != ie.FullyQualifiedTEID { return >pv2.UnexpectedIEError{IEType: fteiduIE.Type} } ip, err := fteiduIE.IPAddress() if err != nil { return err } addr, err := net.ResolveUDPAddr("udp", ip+gtpv2.GTPUPort) if err != nil { return err } bearer.SetRemoteAddress(addr) teid, err := fteiduIE.TEID() if err != nil { return err } bearer.SetOutgoingTEID(teid) it, err := fteiduIE.InterfaceType() if err != nil { return err } session.AddTEID(it, teid) return nil } ================================================ FILE: examples/sgw/s5.go ================================================ package main import ( "fmt" "net" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) func handleCreateSessionResponse(s5cConn *gtpv2.Conn, pgwAddr net.Addr, msg message.Message) error { sgw.loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), pgwAddr) s5Session, err := s5cConn.GetSessionByTEID(msg.TEID(), pgwAddr) if err != nil { return err } // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). csRspFromPGW := msg.(*message.CreateSessionResponse) // check Cause value first. if causeIE := csRspFromPGW.Cause; causeIE != nil { cause, err := causeIE.Cause() if err != nil { return err } if cause != gtpv2.CauseRequestAccepted { s5cConn.RemoveSession(s5Session) // this is not such a fatal error worth stopping the whole program. // in the real case it is better to take some action based on the Cause, though. return >pv2.CauseNotOKError{ MsgType: csRspFromPGW.MessageTypeName(), Cause: cause, Msg: fmt.Sprintf("subscriber: %s", s5Session.IMSI), } } } else { s5cConn.RemoveSession(s5Session) return >pv2.RequiredIEMissingError{ Type: ie.Cause, } } bearer := s5Session.GetDefaultBearer() // retrieve values that P-GW gave. if paaIE := csRspFromPGW.PAA; paaIE != nil { ip, err := paaIE.IPAddress() if err != nil { return err } bearer.SubscriberIP = ip } else { s5cConn.RemoveSession(s5Session) return >pv2.RequiredIEMissingError{Type: ie.PDNAddressAllocation} } if fteidcIE := csRspFromPGW.PGWS5S8FTEIDC; fteidcIE != nil { it, err := fteidcIE.InterfaceType() if err != nil { return err } teid, err := fteidcIE.TEID() if err != nil { return err } s5Session.AddTEID(it, teid) } else { s5cConn.RemoveSession(s5Session) return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} } if brCtxIE := csRspFromPGW.BearerContextsCreated; brCtxIE != nil { for _, childIE := range brCtxIE[0].ChildIEs { switch childIE.Type { case ie.Cause: cause, err := childIE.Cause() if err != nil { return err } if cause != gtpv2.CauseRequestAccepted { s5cConn.RemoveSession(s5Session) return >pv2.CauseNotOKError{ MsgType: csRspFromPGW.MessageTypeName(), Cause: cause, Msg: fmt.Sprintf("subscriber: %s", s5Session.IMSI), } } case ie.EPSBearerID: ebi, err := childIE.EPSBearerID() if err != nil { return err } bearer.EBI = ebi case ie.FullyQualifiedTEID: if err := handleFTEIDU(childIE, s5Session, bearer); err != nil { return err } case ie.ChargingID: cid, err := childIE.ChargingID() if err != nil { return err } bearer.ChargingID = cid } } } else { s5cConn.RemoveSession(s5Session) return >pv2.RequiredIEMissingError{Type: ie.BearerContext} } if err := s5Session.Activate(); err != nil { s5cConn.RemoveSession(s5Session) return err } s11Session, err := sgw.s11Conn.GetSessionByIMSI(s5Session.IMSI) if err != nil { return err } if err := gtpv2.PassMessageTo(s11Session, csRspFromPGW, 5*time.Second); err != nil { return err } return nil } func handleDeleteSessionResponse(s5cConn *gtpv2.Conn, pgwAddr net.Addr, msg message.Message) error { sgw.loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), pgwAddr) s5Session, err := s5cConn.GetSessionByTEID(msg.TEID(), pgwAddr) if err != nil { return err } s11Session, err := sgw.s11Conn.GetSessionByIMSI(s5Session.IMSI) if err != nil { return err } if err := gtpv2.PassMessageTo(s11Session, msg, 5*time.Second); err != nil { return err } // even the cause indicates failure, session should be removed locally. sgw.loggerCh <- fmt.Sprintf("Session deleted for Subscriber: %s", s5Session.IMSI) s5cConn.RemoveSession(s5Session) return nil } func handleDeleteBearerRequest(s5cConn *gtpv2.Conn, pgwAddr net.Addr, msg message.Message) error { sgw.loggerCh <- fmt.Sprintf("Received %s from %s", msg.MessageTypeName(), pgwAddr) s5Session, err := s5cConn.GetSessionByTEID(msg.TEID(), pgwAddr) if err != nil { return err } s11Session, err := sgw.s11Conn.GetSessionByIMSI(s5Session.IMSI) if err != nil { return err } s5cpgwTEID, err := s5Session.GetTEID(gtpv2.IFTypeS5S8PGWGTPC) if err != nil { return err } s11mmeTEID, err := s11Session.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { return err } // assert type to refer to the struct field specific to the message. // in general, no need to check if it can be type-asserted, as long as the MessageType is // specified correctly in AddHandler(). dbReqFromPGW := msg.(*message.DeleteBearerRequest) var dbRspFromSGW *message.DeleteBearerResponse var ebi *ie.IE if ie := dbReqFromPGW.LinkedEBI; ie != nil { ebi = ie } if e := dbReqFromPGW.EBIs; e != nil { ebiIE := e[0] // shouldn't be both. if ebi != nil { dbRspFromSGW = message.NewDeleteBearerResponse( s5cpgwTEID, 0, ie.NewCause(gtpv2.CauseContextNotFound, 0, 0, 0, ebiIE), ) if err := s5cConn.RespondTo(pgwAddr, dbReqFromPGW, dbRspFromSGW); err != nil { return err } return fmt.Errorf( "%T from %s had both Linked EBI and EBIs IE", dbReqFromPGW, pgwAddr, ) } ebi = ebiIE } if ebi == nil { dbRspFromSGW = message.NewDeleteBearerResponse( s5cpgwTEID, 0, ie.NewCause(gtpv2.CauseMandatoryIEMissing, 0, 0, 0, ie.NewEPSBearerID(0), ), ) if err := s5cConn.RespondTo(pgwAddr, dbReqFromPGW, dbRspFromSGW); err != nil { return err } return err } // check if bearer associated with EBI exists or not. _, err = s5Session.LookupBearerByEBI(ebi.MustEPSBearerID()) if err != nil { dbRspFromSGW = message.NewDeleteBearerResponse( s5cpgwTEID, 0, ie.NewCause(gtpv2.CauseContextNotFound, 0, 0, 0, nil), ) if err := s5cConn.RespondTo(pgwAddr, dbReqFromPGW, dbRspFromSGW); err != nil { return err } return err } // forward to MME seq, err := sgw.s11Conn.DeleteBearer(s11mmeTEID, s11Session, ebi) if err != nil { return err } // wait for response from MME for 5 seconds incomingMsg, err := s5Session.WaitMessage(seq, 5*time.Second) if err != nil { dbRspFromSGW = message.NewDeleteBearerResponse( s5cpgwTEID, 0, ie.NewCause(gtpv2.CauseNoResourcesAvailable, 0, 0, 0, nil), ) if err := s5cConn.RespondTo(pgwAddr, dbReqFromPGW, dbRspFromSGW); err != nil { return err } // remove anyway, as P-GW no longer keeps bearer locally s5Session.RemoveBearerByEBI(ebi.MustEPSBearerID()) s11Session.RemoveBearerByEBI(ebi.MustEPSBearerID()) return err } switch m := incomingMsg.(type) { case *message.DeleteBearerResponse: // move forward dbRspFromSGW = m default: return >pv2.UnexpectedTypeError{Msg: incomingMsg} } dbRspFromSGW.SetTEID(s5cpgwTEID) if err := s5cConn.RespondTo(pgwAddr, msg, dbRspFromSGW); err != nil { return err } s5Session.RemoveBearerByEBI(ebi.MustEPSBearerID()) s11Session.RemoveBearerByEBI(ebi.MustEPSBearerID()) return nil } ================================================ FILE: examples/utils/mac_local_host_enabler.sh ================================================ # This script will enable 127.0.0.1 up to 127.0.0.256 interfaces for ((i=2;i<256;i++)) do sudo ifconfig lo0 alias 127.0.0.$i up done ================================================ FILE: go.mod ================================================ module github.com/wmnsk/go-gtp go 1.25.0 require ( github.com/golang/protobuf v1.5.4 github.com/google/go-cmp v0.7.0 github.com/pascaldekloe/goe v0.1.1 github.com/prometheus/client_golang v1.23.2 github.com/vishvananda/netlink v1.3.1 golang.org/x/net v0.51.0 google.golang.org/grpc v1.79.2 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect google.golang.org/protobuf v1.36.11 // indirect ) ================================================ FILE: go.sum ================================================ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/pascaldekloe/goe v0.1.1 h1:Ah6WQ56rZONR3RW3qWa2NCZ6JAVvSpUcoLBaOmYFt9Q= github.com/pascaldekloe/goe v0.1.1/go.mod h1:KSyfaxQOh0HZPjDP1FL/kFtbqYqrALJTaMafFUIccqU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: gtp.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtp import ( v0msg "github.com/wmnsk/go-gtp/gtpv0/message" v1msg "github.com/wmnsk/go-gtp/gtpv1/message" v2msg "github.com/wmnsk/go-gtp/gtpv2/message" ) // Message is an interface that defines all versions of GTP message. type Message interface { MarshalTo([]byte) error UnmarshalBinary(b []byte) error MarshalLen() int Version() int MessageType() uint8 MessageTypeName() string // deprecated SerializeTo([]byte) error DecodeFromBytes(b []byte) error } // Marshal returns the byte sequence generated from a Message instance. // Better to use (*MessageName).Marshal instead if you know the name of message to be serialized. func Marshal(m Message) ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // Parse decodes given bytes as Message. func Parse(b []byte) (Message, error) { if len(b) < 8 { return nil, ErrTooShortToParse } switch b[0] >> 5 { case 0: return v0msg.Parse(b) case 1: return v1msg.Parse(b) case 2: return v2msg.Parse(b) default: return nil, ErrInvalidVersion } } ================================================ FILE: gtp_fuzz_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtp_test import ( "testing" "github.com/wmnsk/go-gtp" ) func FuzzParse(f *testing.F) { f.Fuzz(func(t *testing.T, b []byte) { if _, err := gtp.Parse(b); err != nil { t.Skip() } }) } ================================================ FILE: gtp_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtp import ( "testing" "github.com/google/go-cmp/cmp" "github.com/pascaldekloe/goe/verify" v0msg "github.com/wmnsk/go-gtp/gtpv0/message" v1msg "github.com/wmnsk/go-gtp/gtpv1/message" v2ie "github.com/wmnsk/go-gtp/gtpv2/ie" v2msg "github.com/wmnsk/go-gtp/gtpv2/message" ) var v0flow = struct { seq uint16 label uint16 tid uint64 }{1, 0, 0x2143658709214355} func TestMessage(t *testing.T) { cases := []struct { description string structured Message serialized []byte }{ { "GTPv0 Echo Request", v0msg.NewEchoRequest(v0flow.seq, v0flow.label, v0flow.tid), []byte{ 0x1e, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, }, }, { "GTPv1 Echo Request", v1msg.NewEchoRequest(0), []byte{ 0x32, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, { "GTPv2 Echo Request", v2msg.NewEchoRequest(0, v2ie.NewRecovery(0x80)), []byte{ 0x40, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x80, }, }, } for _, c := range cases { t.Run("Encode/"+c.description, func(t *testing.T) { got, err := Marshal(c.structured) if err != nil { t.Fatal(err) } if diff := cmp.Diff(got, c.serialized); diff != "" { t.Error(diff) } }) t.Run("Parse/"+c.description, func(t *testing.T) { v, err := Parse(c.serialized) if err != nil { t.Fatal(err) } if got, want := v, c.structured; !verify.Values(t, "", got, want) { t.Fail() } }) t.Run("MarshalLen/"+c.description, func(t *testing.T) { if got, want := c.structured.MarshalLen(), len(c.serialized); got != want { t.Fatalf("got %v want %v", got, want) } }) t.Run("Interface/"+c.description, func(t *testing.T) { decoded, err := Parse(c.serialized) if err != nil { t.Fatal(err) } if got, want := decoded.Version(), c.structured.Version(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := decoded.MessageType(), c.structured.MessageType(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := decoded.MessageTypeName(), c.structured.MessageTypeName(); got != want { t.Fatalf("got %v want %v", got, want) } }) } } ================================================ FILE: gtpv0/README.md ================================================ # v0: GTPv0 in Golang Package v0 provides simple and painless handling of GTPv0 protocol in pure Golang. ## Getting Started This package is still under construction. See the source codes for what you can do with the current implementation. ### Creating a PDP Context as a client _NOT IMPLEMENTED YET!_ ### Waiting for a PDP Context to be created as a server _NOT IMPLEMENTED YET!_ ### Opening a U-Plane connection _NOT IMPLEMENTED YET!_ ## Supported Features The following Messages marked with "Yes" are currently available with their own useful constructors. _Even there are some missing Messages, you can create any kind of Message by using `message.NewGeneric()`._ ### Messages | ID | Name | Supported | |---------|---------------------------------------------|-----------| | 0 | (Spare/Reserved) | - | | 1 | Echo Request | Yes | | 2 | Echo Response | Yes | | 3 | Version Not Supported | | | 4 | Node Alive Request | | | 5 | Node Alive Response | | | 6 | Redirection Request | | | 7 | Redirection Response | | | 8-15 | (Spare/Reserved) | - | | 16 | Create PDP Context Request | Yes | | 17 | Create PDP Context Response | Yes | | 18 | Update PDP Context Request | Yes | | 19 | Update PDP Context Response | Yes | | 20 | Delete PDP Context Request | Yes | | 21 | Delete PDP Context Response | Yes | | 22 | Create AA PDP Context Request | | | 23 | Create AA PDP Context Response | | | 24 | Delete AA PDP Context Request | | | 25 | Delete AA PDP Context Response | | | 26 | Error Indication | | | 27 | PDU Notification Request | | | 28 | PDU Notification Response | | | 29 | PDU Notification Reject Request | | | 30 | PDU Notification Reject Response | | | 31 | (Spare/Reserved) | - | | 32 | Send Routeing Information for GPRS Request | | | 33 | Send Routeing Information for GPRS Response | | | 34 | Failure Report Request | | | 35 | Failure Report Response | | | 36 | Note MS GPRS Present Request | | | 37 | Note MS GPRS Present Response | | | 38-47 | (Spare/Reserved) | - | | 48 | Identification Request | | | 49 | Identification Response | | | 50 | SGSN Context Request | | | 51 | SGSN Context Response | | | 52 | SGSN Context Acknowledge | | | 53-239 | (Spare/Reserved) | - | | 240 | Data Record Transfer Request | | | 241 | Data Record Transfer Response | | | 242-254 | (Spare/Reserved) | - | | 255 | T-PDU | Yes | ### Information Elements The following Information Elements marked with "Yes" are currently available with their own useful constructors. _Even there are some missing IEs, you can create any kind of IEs by using `ie.New()` function or by initializing ie.IE directly._ | ID | Name | Supported | |---------|----------------------------------------|-----------| | 0 | (Spare/Reserved) | - | | 1 | Cause | Yes | | 2 | IMSI | Yes | | 3 | Routeing Area Identity (RAI) | Yes | | 4 | Temporary Logical Link Identity (TLLI) | Yes | | 5 | Packet TMSI (P-TMSI) | Yes | | 6 | Quality of Service (QoS) Profile | Yes | | 7 | (Spare/Reserved) | - | | 8 | Reordering Required | Yes | | 9 | Authentication Triplet | | | 10 | (Spare/Reserved) | - | | 11 | MAP Cause | | | 12 | P-TMSI Signature | Yes | | 13 | MS Validated | | | 14 | Recovery | Yes | | 15 | Selection mode | Yes | | 16 | Flow Label Data I | Yes | | 17 | Flow Label Signalling | Yes | | 18 | Flow Label Data II | Yes | | 19 | MS Not Reachable Reason | Yes | | 20-126 | (Spare/Reserved) | - | | 127 | Charging ID | Yes | | 128 | End User Address | Yes | | 129 | MM Context | | | 130 | PDP Context | | | 131 | Access Point Name | Yes | | 132 | Protocol Configuration Options | | | 133 | GSN Address | Yes | | 134 | MSISDN | Yes | | 135-250 | (Spare/Reserved) | - | | 251 | Charging Gateway Address | Yes | | 252-254 | (Spare/Reserved) | - | | 255 | Private Extension | Yes | ================================================ FILE: gtpv0/constants.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv0 // Cause definitions. const ( CauseRequestIMSI uint8 = 0 CauseRequestIMEI uint8 = 1 CauseRequestIMSIandIMEI uint8 = 2 CauseNoIdentityNeeded uint8 = 3 CauseRequestAccepted uint8 = 128 CauseNonExistent uint8 = 192 CauseInvalidMessageFormat uint8 = 193 CauseIMSINotKnown uint8 = 194 CauseMSIsGPRSDetached uint8 = 195 CauseMSIsNotGPRSResponding uint8 = 196 CauseMSRefuses uint8 = 197 CauseVersionNotSupported uint8 = 198 CauseNoResourcesAvailable uint8 = 199 CauseServiceNotSupported uint8 = 200 CauseMandatoryIEIncorrect uint8 = 201 CauseMandatoryIEMissing uint8 = 202 CauseOptionalIEIncorrect uint8 = 203 CauseSystemFailure uint8 = 204 CauseRoamingRestriction uint8 = 205 CausePTMSISignatureMismatch uint8 = 206 CauseGPRSConnectionSuspended uint8 = 207 CauseAuthenticationFailure uint8 = 208 CauseUserAuthenticationFailed uint8 = 209 ) // PDP Type Organization definitions. const ( PDPTypeETSI uint8 = iota | 0xf0 PDPTypeIETF ) // SelectionMode definitions. const ( SelectionModeMSorNetworkProvidedAPNSubscribedVerified uint8 = iota | 0xf0 SelectionModeMSProvidedAPNSubscriptionNotVerified SelectionModeNetworkProvidedAPNSubscriptionNotVerified ) ================================================ FILE: gtpv0/doc.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Package gtpv0 provides simple and painless handling of GTPv0 protocol in pure Golang. // // This package is still under construction. The networking feature would be available in the future. // Please see README.md for detailed usage of the APIs provided by this package. // // https://github.com/wmnsk/go-gtp/blob/main/gtpv0/README.md package gtpv0 ================================================ FILE: gtpv0/ie/apn.go ================================================ // Copyright 2019-2024 go-gtp. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "strings" ) // NewAccessPointName creates a new AccessPointName IE. func NewAccessPointName(apn string) *IE { i := New(AccessPointName, make([]byte, len(apn)+1)) var offset = 0 for _, label := range strings.Split(apn, ".") { l := len(label) i.Payload[offset] = uint8(l) copy(i.Payload[offset+1:], label) offset += l + 1 } return i } // AccessPointName returns AccessPointName in string if type of IE matches. func (i *IE) AccessPointName() (string, error) { if i.Type != AccessPointName { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return "", io.ErrUnexpectedEOF } var ( apn []string offset int ) max := len(i.Payload) for { if offset >= max { break } l := int(i.Payload[offset]) if offset+l+1 >= max { return "", io.ErrUnexpectedEOF } apn = append(apn, string(i.Payload[offset+1:offset+l+1])) offset += l + 1 } return strings.Join(apn, "."), nil } // MustAccessPointName returns AccessPointName in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustAccessPointName() string { v, _ := i.AccessPointName() return v } ================================================ FILE: gtpv0/ie/cause.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewCause creates a new Cause IE. func NewCause(cause uint8) *IE { return newUint8ValIE(Cause, cause) } // Cause returns Cause value if type matches. func (i *IE) Cause() (uint8, error) { if i.Type != Cause { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustCause returns Cause in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustCause() uint8 { v, _ := i.Cause() return v } ================================================ FILE: gtpv0/ie/charging-gateway-address.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "net" ) // NewChargingGatewayAddress creates a new ChargingGatewayAddress IE from string. func NewChargingGatewayAddress(addr string) *IE { ip := net.ParseIP(addr) v4 := ip.To4() // IPv4 if v4 != nil { return New(ChargingGatewayAddress, v4) } // IPv6 return New(ChargingGatewayAddress, ip) } // ChargingGatewayAddress returns ChargingGatewayAddress value if type matches. func (i *IE) ChargingGatewayAddress() (string, error) { if i.Type != ChargingGatewayAddress { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return "", io.ErrUnexpectedEOF } return net.IP(i.Payload).String(), nil } // MustChargingGatewayAddress returns ChargingGatewayAddress in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustChargingGatewayAddress() string { v, _ := i.ChargingGatewayAddress() return v } ================================================ FILE: gtpv0/ie/charging-id.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewChargingID creates a new ChargingID IE. func NewChargingID(id uint32) *IE { return newUint32ValIE(ChargingID, id) } // ChargingID returns ChargingID value in uint32 if type matches. func (i *IE) ChargingID() (uint32, error) { if i.Type != ChargingID { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload), nil } // MustChargingID returns ChargingID in uint32 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustChargingID() uint32 { v, _ := i.ChargingID() return v } ================================================ FILE: gtpv0/ie/end-user-address.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "net" ) // PDP Type Organization definitions. const ( pdpTypeETSI uint8 = iota | 0xf0 pdpTypeIETF ) // NewEndUserAddress creates a new EndUserAddress IE from the given IP Address in string. // // The addr can be either IPv4 or IPv6. If the address type is PPP, // just put "ppp" in addr parameter or use NewEndUserAddressPPP func instead. func NewEndUserAddress(addr string) *IE { if addr == "ppp" { return NewEndUserAddressPPP() } ip := net.ParseIP(addr) v4 := ip.To4() // IPv4 if v4 != nil { return newEUAddrV4(v4) } return newEUAddrV6(ip) } // NewEndUserAddressIPv4 creates a new EndUserAddress IE with IPv4. func NewEndUserAddressIPv4(addr string) *IE { v4 := net.ParseIP(addr).To4() if v4 == nil { return New(EndUserAddress, []byte{0xf1, 0x21}) } return newEUAddrV4(v4) } // NewEndUserAddressIPv6 creates a new EndUserAddress IE with IPv6. func NewEndUserAddressIPv6(addr string) *IE { v6 := net.ParseIP(addr).To16() if v6 == nil { return New(EndUserAddress, []byte{0xf1, 0x57}) } return newEUAddrV6(v6) } func newEUAddrV4(v4 []byte) *IE { e := New( EndUserAddress, make([]byte, 6), ) e.Payload[0] = pdpTypeIETF e.Payload[1] = 0x21 copy(e.Payload[2:], v4) return e } func newEUAddrV6(v6 []byte) *IE { e := New( EndUserAddress, make([]byte, 18), ) e.Payload = make([]byte, 18) e.Payload[0] = pdpTypeIETF e.Payload[1] = 0x57 copy(e.Payload[2:], v6) return e } // NewEndUserAddressPPP creates a new EndUserAddress IE with PPP. func NewEndUserAddressPPP() *IE { e := New(EndUserAddress, make([]byte, 2)) e.Payload[0] = pdpTypeETSI e.Payload[1] = pdpTypeIETF e.SetLength() return e } // EndUserAddress returns EndUserAddress value if type matches. func (i *IE) EndUserAddress() ([]byte, error) { if i.Type != EndUserAddress { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustEndUserAddress returns EndUserAddress in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustEndUserAddress() []byte { v, _ := i.EndUserAddress() return v } // PDPTypeOrganization returns PDPTypeOrganization if type matches. func (i *IE) PDPTypeOrganization() (uint8, error) { if i.Type != EndUserAddress { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustPDPTypeOrganization returns PDPTypeOrganization in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPDPTypeOrganization() uint8 { v, _ := i.PDPTypeOrganization() return v } // PDPTypeNumber returns PDPTypeNumber if type matches. func (i *IE) PDPTypeNumber() (uint8, error) { if i.Type != EndUserAddress { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return i.Payload[1], nil } // MustPDPTypeNumber returns PDPTypeNumber in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPDPTypeNumber() uint8 { v, _ := i.PDPTypeNumber() return v } // IPAddress returns IPAddress if type matches. func (i *IE) IPAddress() (string, error) { if len(i.Payload) < 4 { return "", io.ErrUnexpectedEOF } switch i.Type { case EndUserAddress: if i.MustPDPTypeOrganization() != pdpTypeIETF { return "", ErrMalformed } if len(i.Payload) < 3 { return "", io.ErrUnexpectedEOF } return net.IP(i.Payload[2:]).String(), nil case GSNAddress: return net.IP(i.Payload).String(), nil default: return "", &InvalidTypeError{Type: i.Type} } } // MustIPAddress returns IPAddress in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustIPAddress() string { v, _ := i.IPAddress() return v } ================================================ FILE: gtpv0/ie/errors.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "errors" "fmt" ) // Error definitions. var ( ErrInvalidLength = errors.New("got invalid length") ErrTooShortToMarshal = errors.New("too short to Marshal") ErrTooShortToParse = errors.New("too short to Parse as GTPv0 IE") ErrMalformed = errors.New("malformed IE") ) // InvalidTypeError indicates the type of IE is invalid. type InvalidTypeError struct { Type uint8 } // Error returns message with the invalid type given. func (e *InvalidTypeError) Error() string { return fmt.Sprintf("got invalid type: %v", e.Type) } ================================================ FILE: gtpv0/ie/flow-label.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewFlowLabelDataI creates a new FlowLabelDataI IE. func NewFlowLabelDataI(label uint16) *IE { return newUint16ValIE(FlowLabelDataI, label) } // FlowLabelDataI returns FlowLabelDataI if type matches. func (i *IE) FlowLabelDataI() (uint16, error) { if i.Type != FlowLabelDataI { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload), nil } // MustFlowLabelDataI returns FlowLabelDataI in uint16 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustFlowLabelDataI() uint16 { v, _ := i.FlowLabelDataI() return v } // NewFlowLabelSignalling creates a new FlowLabelSignalling IE. func NewFlowLabelSignalling(label uint16) *IE { return newUint16ValIE(FlowLabelSignalling, label) } // FlowLabelSignalling returns FlowLabelSignalling if type matches. func (i *IE) FlowLabelSignalling() (uint16, error) { if i.Type != FlowLabelSignalling { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload), nil } // MustFlowLabelSignalling returns FlowLabelSignalling in uint16 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustFlowLabelSignalling() uint16 { v, _ := i.FlowLabelSignalling() return v } // NewFlowLabelDataII creates a new FlowLabelDataII IE. func NewFlowLabelDataII(nsapi uint8, label uint16) *IE { i := New(FlowLabelDataII, make([]byte, 3)) i.Payload[0] = nsapi | 0xf0 binary.BigEndian.PutUint16(i.Payload[1:3], label) return i } // FlowLabelDataII returns FlowLabelDataII if type matches. func (i *IE) FlowLabelDataII() ([]byte, error) { if i.Type != FlowLabelDataII { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustFlowLabelDataII returns FlowLabelDataII in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustFlowLabelDataII() []byte { v, _ := i.FlowLabelDataII() return v } // NSAPI returns NSAPI in uint8 if type matches. func (i *IE) NSAPI() (uint8, error) { if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } switch i.Type { case FlowLabelDataII: return i.Payload[0] & 0x0f, nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustNSAPI returns NSAPI in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustNSAPI() uint8 { v, _ := i.NSAPI() return v } // FlowLabelData returns FlowLabelData in uint16 if type matches. func (i *IE) FlowLabelData() (uint16, error) { if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } switch i.Type { case FlowLabelDataI: return i.FlowLabelDataI() case FlowLabelSignalling: return i.FlowLabelSignalling() case FlowLabelDataII: if len(i.Payload) < 3 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[1:3]), nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustFlowLabelData returns FlowLabelData in uint16 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustFlowLabelData() uint16 { v, _ := i.FlowLabelData() return v } ================================================ FILE: gtpv0/ie/gsn-address.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "net" ) // NewGSNAddress creates a new GSNAddress IE from string. func NewGSNAddress(addr string) *IE { ip := net.ParseIP(addr) v4 := ip.To4() // IPv4 if v4 != nil { return New(GSNAddress, v4) } // IPv6 return New(GSNAddress, ip) } // GSNAddress returns GSNAddress value if type matches. func (i *IE) GSNAddress() (string, error) { if i.Type != GSNAddress { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return "", io.ErrUnexpectedEOF } return net.IP(i.Payload).String(), nil } // MustGSNAddress returns GSNAddress in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustGSNAddress() string { v, _ := i.GSNAddress() return v } ================================================ FILE: gtpv0/ie/ie.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. /* Package ie provides encoding/decoding feature of GTPv0 Information Elements. */ package ie import ( "encoding/binary" "fmt" ) // TV IE definitions. const ( Cause uint8 = 1 IMSI uint8 = 2 RouteingAreaIdentity uint8 = 3 TemporaryLogicalLinkIdentity uint8 = 4 PacketTMSI uint8 = 5 QualityOfServiceProfile uint8 = 6 ReorderingRequired uint8 = 8 AuthenticationTriplet uint8 = 9 MAPCause uint8 = 11 PTMSISignature uint8 = 12 MSValidated uint8 = 13 Recovery uint8 = 14 SelectionMode uint8 = 15 FlowLabelDataI uint8 = 16 FlowLabelSignalling uint8 = 17 FlowLabelDataII uint8 = 18 MSNotReachableReason uint8 = 19 ChargingID uint8 = 127 ) // TLV IE definitions. const ( EndUserAddress uint8 = 128 MMContext uint8 = 129 PDPContext uint8 = 130 AccessPointName uint8 = 131 ProtocolConfigurationOptions uint8 = 132 GSNAddress uint8 = 133 MSISDN uint8 = 134 ChargingGatewayAddress uint8 = 251 PrivateExtension uint8 = 255 ) // IE is a GTPv0 Information Element. type IE struct { Type uint8 Length uint16 Payload []byte } // New creates new IE. func New(t uint8, p []byte) *IE { i := &IE{Type: t, Payload: p} i.SetLength() return i } // Marshal returns the byte sequence generated from an IE instance. func (i *IE) Marshal() ([]byte, error) { b := make([]byte, i.MarshalLen()) if err := i.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (i *IE) MarshalTo(b []byte) error { if len(b) < i.MarshalLen() { return ErrTooShortToMarshal } var offset = 1 b[0] = i.Type if !i.IsTV() { binary.BigEndian.PutUint16(b[1:3], i.Length) offset += 2 } copy(b[offset:i.MarshalLen()], i.Payload) return nil } // Parse Parses given byte sequence as a GTPv0 Information Element. func Parse(b []byte) (*IE, error) { i := &IE{} if err := i.UnmarshalBinary(b); err != nil { return nil, err } return i, nil } // UnmarshalBinary sets the values retrieved from byte sequence in GTPv0 IE. func (i *IE) UnmarshalBinary(b []byte) error { if len(b) < 2 { return ErrTooShortToParse } i.Type = b[0] if i.IsTV() { return parseTVFromBytes(i, b) } return parseTLVFromBytes(i, b) } func parseTVFromBytes(i *IE, b []byte) error { l := len(b) if l < 2 { return ErrTooShortToParse } if i.MarshalLen() > l { return ErrInvalidLength } i.Length = 0 i.Payload = b[1:i.MarshalLen()] return nil } func parseTLVFromBytes(i *IE, b []byte) error { l := len(b) if l < 3 { return ErrTooShortToParse } i.Length = binary.BigEndian.Uint16(b[1:3]) if int(i.Length)+3 > l { return ErrInvalidLength } i.Payload = b[3 : 3+int(i.Length)] return nil } var tvLengthMap = map[uint8]int{ 0: 0, // Reserved 1: 1, // Cause 2: 8, // IMSI 3: 6, // RAI 4: 4, // TLLI 5: 4, // P-TMSI 6: 3, // QoS 8: 1, // Reordering Required 9: 28, // Authentication Triplet 11: 1, // MAP Cause 12: 3, // P-TMSI Signature 13: 1, // MS Validated 14: 1, // Recovery 15: 1, // Selection Mode 16: 2, // Flow Label Data I 17: 2, // Flow Label Signalling 18: 3, // Flow Label Data II 19: 1, // MS Not Reachable Reason 127: 4, // Charging ID } // IsTV checks if a IE is TV format. If false, it indicates the IE has Length inside. func (i *IE) IsTV() bool { return int(i.Type) < 0x80 } // MarshalLen returns the serial length of IE. func (i *IE) MarshalLen() int { if l, ok := tvLengthMap[i.Type]; ok { return l + 1 } if i.Type < 128 { return 1 + len(i.Payload) } return 3 + len(i.Payload) } // SetLength sets the length in Length field. func (i *IE) SetLength() { if _, ok := tvLengthMap[i.Type]; ok { i.Length = 0 return } i.Length = uint16(len(i.Payload)) } // Name returns the name of IE in string. func (i *IE) Name() string { if n, ok := ieTypeNameMap[i.Type]; ok { return n } return "Undefined" } // String returns the GTPv0 IE values in human readable format. func (i *IE) String() string { if i == nil { return "nil" } return fmt.Sprintf("{%s: {Type: %d, Length: %d, Payload: %#v}}", i.Name(), i.Type, i.Length, i.Payload, ) } // ParseMultiIEs Parses multiple (unspecified number of) IEs to []*IE at a time. func ParseMultiIEs(b []byte) ([]*IE, error) { var ies []*IE for { if len(b) == 0 { break } i, err := Parse(b) if err != nil { return nil, err } ies = append(ies, i) b = b[i.MarshalLen():] continue } return ies, nil } func newUint8ValIE(t, v uint8) *IE { return New(t, []byte{v}) } func newUint16ValIE(t uint8, v uint16) *IE { i := New(t, make([]byte, 2)) binary.BigEndian.PutUint16(i.Payload, v) return i } func newUint32ValIE(t uint8, v uint32) *IE { i := New(t, make([]byte, 4)) binary.BigEndian.PutUint32(i.Payload, v) return i } // left for future use. // func newStringIE(t uint8, str string) *IE { // return New(t, []byte(str)) // } var ieTypeNameMap = map[uint8]string{ 1: "Cause", 2: "IMSI", 3: "RouteingAreaIdentity", 4: "TemporaryLogicalLinkIdentity", 5: "PacketTMSI", 6: "QualityOfServiceProfile", 8: "ReorderingRequired", 9: "AuthenticationTriplet", 11: "MAPCause", 12: "PTMSISignature", 13: "MSValidated", 14: "Recovery", 15: "SelectionMode", 16: "FlowLabelDataI", 17: "FlowLabelSignalling", 18: "FlowLabelDataII", 19: "MSNotReachableReason", 127: "ChargingID", 128: "EndUserAddress", 129: "MMContext", 130: "PDPContext", 131: "AccessPointName", 132: "ProtocolConfigurationOptions", 133: "GSNAddress", 134: "MSISDN", 251: "ChargingGatewayAddress", 255: "PrivateExtension", } ================================================ FILE: gtpv0/ie/ie_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "log" // Serialize serializes IE into bytes. // // Deprecated: use IE.Marshal instead. func (i *IE) Serialize() ([]byte, error) { log.Println("IE.Serialize is deprecated. use IE.Marshal instead") return i.Marshal() } // SerializeTo serializes IE into bytes given as b. // // Deprecated: use IE.MarshalTo instead. func (i *IE) SerializeTo(b []byte) error { log.Println("IE.SerializeTo is deprecated. use IE.MarshalTo instead") return i.MarshalTo(b) } // Decode decodes bytes as IE. // // Deprecated: use Parse instead. func Decode(b []byte) (*IE, error) { log.Println("Decode is deprecated. use Parse instead") return Parse(b) } // DecodeFromBytes decodes bytes as IE. // // Deprecated: use IE.UnmarshalBinary instead. func (i *IE) DecodeFromBytes(b []byte) error { log.Println("IE.DecodeFromBytes is deprecated. use IE.UnmarshalBinary instead") return i.UnmarshalBinary(b) } // Len returns the actual length of IE. // // Deprecated: use IE.MarshalLen instead. func (i *IE) Len() int { log.Println("IE.Len is deprecated. use IE.MarshalLen instead") return i.MarshalLen() } ================================================ FILE: gtpv0/ie/ie_fuzz_test.go ================================================ package ie_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0/ie" ) func FuzzParse(f *testing.F) { f.Fuzz(func(t *testing.T, b []byte) { if _, err := ie.Parse(b); err != nil { t.Skip() } }) } ================================================ FILE: gtpv0/ie/ie_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/wmnsk/go-gtp/gtpv0" "github.com/wmnsk/go-gtp/gtpv0/ie" ) func TestIE(t *testing.T) { cases := []struct { description string structured *ie.IE Serialized []byte }{ { "Cause", ie.NewCause(gtpv0.CauseRequestAccepted), []byte{0x01, 0x80}, }, { "IMSI", ie.NewIMSI("123450123456789"), []byte{0x02, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9}, }, { "RAI", ie.NewRouteingAreaIdentity("123", "45", 0x1111, 0x22), []byte{0x03, 0x21, 0xf3, 0x54, 0x11, 0x11, 0x22}, }, { "TLLI", ie.NewTemporaryLogicalLinkIdentity(0xff00ff00), []byte{0x04, 0xff, 0x00, 0xff, 0x00}, }, { "PacketTMSI", ie.NewPacketTMSI(0xdeadbeef), []byte{0x05, 0xde, 0xad, 0xbe, 0xef}, }, { // XXX - not implemented fully "QoS Profile", ie.NewQualityOfServiceProfile(1, 1, 1, 1, 1), []byte{0x06, 0x09, 0x11, 0x01}, }, { "ReorderingRequired", ie.NewReorderingRequired(false), []byte{0x08, 0xfe}, }, /* XXX - not implemented { "AuthenticationTriplet", ie.NewAuthenticationTriplet(), []byte{}, }, { "MAPCause", ie.NewMAPCause(), []byte{}, }, { "PacketTMSISignature", ie.NewPacketTMSISignature(), []byte{}, }, { "MSValidated", ie.NewMSValidated(), []byte{}, },*/ { "PTMSISignature", ie.NewPTMSISignature(0xbeebee), []byte{0x0c, 0xbe, 0xeb, 0xee}, }, { "Recovery", ie.NewRecovery(0x80), []byte{0x0e, 0x80}, }, { "SelectionMode", ie.NewSelectionMode(0xff), []byte{0x0f, 0xff}, }, { "FlowLabelDataI", ie.NewFlowLabelDataI(0x0001), []byte{0x10, 0x00, 0x01}, }, { "FlowLabelSignalling", ie.NewFlowLabelSignalling(0x0001), []byte{0x11, 0x00, 0x01}, }, { "FlowLabelDataII", ie.NewFlowLabelDataII(5, 0x0001), []byte{0x12, 0xf5, 0x00, 0x01}, }, { "MSNotReachableReason", ie.NewMSNotReachableReason(0xff), []byte{0x13, 0xff}, }, { "ChargingID", ie.NewChargingID(0xff00ff00), []byte{0x7f, 0xff, 0x00, 0xff, 0x00}, }, { "EndUserAddress/v4", ie.NewEndUserAddressIPv4("1.1.1.1"), []byte{ // Type, Length 0x80, 0x00, 0x06, // Value 0xf1, 0x21, 0x01, 0x01, 0x01, 0x01, }, }, { "EndUserAddress/v6", ie.NewEndUserAddressIPv6("2001::1"), []byte{ // Type, Length 0x80, 0x00, 0x12, // Value 0xf1, 0x57, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }, }, { "EndUserAddress/ppp", ie.NewEndUserAddressPPP(), []byte{ // Type, Length 0x80, 0x00, 0x02, // Value 0xf0, 0xf1, }, }, /* XXX - not implemented { "MMContext", ie.NewMMContext(), []byte{ // Type, Length 0x81, 0x00, 0x00, // Value }, }, { "PDPContext", ie.NewPDPContext(), []byte{ // Type, Length 0x81, 0x00, 0x00, // Value }, }, */ { "AccessPointName", ie.NewAccessPointName("some.apn.example"), []byte{ // Type, Length 0x83, 0x00, 0x11, // Value 0x04, 0x73, 0x6f, 0x6d, 0x65, 0x03, 0x61, 0x70, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, }, }, /* XXX - not implemented { "PCO", ie.NewProtocolConfigurationOption(), []byte{}, }, */ { "GSNAddress/v4", ie.NewGSNAddress("1.1.1.1"), []byte{ // Type, Length 0x85, 0x00, 0x04, // Value 0x01, 0x01, 0x01, 0x01, }, }, { "GSNAddress/v6", ie.NewGSNAddress("2001::1"), []byte{ // Type, Length 0x85, 0x00, 0x10, // Value 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }, }, { "MSISDN", ie.NewMSISDN("819012345678"), []byte{ // Type, Length 0x86, 0x00, 0x07, // Value 0x91, 0x18, 0x09, 0x21, 0x43, 0x65, 0x87, }, }, { "ChargingGatewayAddress/v4", ie.NewChargingGatewayAddress("1.1.1.1"), []byte{ // Type, Length 0xfb, 0x00, 0x04, // Value 0x01, 0x01, 0x01, 0x01, }, }, { "ChargingGatewayAddress/v6", ie.NewChargingGatewayAddress("2001::1"), []byte{ // Type, Length 0xfb, 0x00, 0x10, // Value 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }, }, { "PrivateExtension", ie.NewPrivateExtension(0x0080, []byte{0xde, 0xad, 0xbe, 0xef}), []byte{ // Type, Length 0xff, 0x00, 0x06, // Value 0x00, 0x80, 0xde, 0xad, 0xbe, 0xef, }, }, } for _, c := range cases { t.Run("Marshal/"+c.description, func(t *testing.T) { got, err := c.structured.Marshal() if err != nil { t.Fatal(err) } if diff := cmp.Diff(got, c.Serialized); diff != "" { t.Error(diff) } }) t.Run("Parse/"+c.description, func(t *testing.T) { got, err := ie.Parse(c.Serialized) if err != nil { t.Fatal(err) } if diff := cmp.Diff(got, c.structured); diff != "" { t.Error(err) } }) } } ================================================ FILE: gtpv0/ie/imsi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewIMSI creates a new IMSI IE. func NewIMSI(imsi string) *IE { i, err := utils.StrToSwappedBytes(imsi, "f") if err != nil { return New(IMSI, nil) } return New(IMSI, i) } // IMSI returns IMSI value in string if type matches. func (i *IE) IMSI() (string, error) { if i.Type != IMSI { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return "", io.ErrUnexpectedEOF } return utils.SwappedBytesToStr(i.Payload, true), nil } // MustIMSI returns IMSI in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustIMSI() string { v, _ := i.IMSI() return v } ================================================ FILE: gtpv0/ie/ms-not-reachable-reason.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewMSNotReachableReason creates a new MSNotReachableReason IE. func NewMSNotReachableReason(reason uint8) *IE { return newUint8ValIE(MSNotReachableReason, reason) } // MSNotReachableReason returns MSNotReachableReason value if type matches. func (i *IE) MSNotReachableReason() (uint8, error) { if i.Type != MSNotReachableReason { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustMSNotReachableReason returns MSNotReachableReason in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustMSNotReachableReason() uint8 { v, _ := i.MSNotReachableReason() return v } ================================================ FILE: gtpv0/ie/msisdn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewMSISDN creates a new MSISDN IE. func NewMSISDN(msisdn string) *IE { i, err := utils.StrToSwappedBytes("19"+msisdn, "f") if err != nil { return nil } return New(MSISDN, i) } // MSISDN returns MSISDN value if type matches. func (i *IE) MSISDN() (string, error) { if i.Type != MSISDN { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return "", io.ErrUnexpectedEOF } return utils.SwappedBytesToStr(i.Payload[1:], false), nil } // MustMSISDN returns MSISDN in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustMSISDN() string { v, _ := i.MSISDN() return v } ================================================ FILE: gtpv0/ie/p-tmsi-signature.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewPTMSISignature creates a new PTMSISignature IE. func NewPTMSISignature(sig uint32) *IE { return New(PTMSISignature, utils.Uint32To24(sig)) } // PTMSISignature returns PTMSISignature value in uint32 if type matches. func (i *IE) PTMSISignature() (uint32, error) { if i.Type != PTMSISignature { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 3 { return 0, io.ErrUnexpectedEOF } return utils.Uint24To32(i.Payload), nil } // MustPTMSISignature returns PTMSISignature in uint32 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPTMSISignature() uint32 { v, _ := i.PTMSISignature() return v } ================================================ FILE: gtpv0/ie/p-tmsi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewPacketTMSI creates a new PacketTMSI IE. func NewPacketTMSI(ptmsi uint32) *IE { return newUint32ValIE(PacketTMSI, ptmsi) } // PacketTMSI returns PacketTMSI value in uint32 if type matches. func (i *IE) PacketTMSI() (uint32, error) { if i.Type != PacketTMSI { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload), nil } // MustPacketTMSI returns PacketTMSI in uint32 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPacketTMSI() uint32 { v, _ := i.PacketTMSI() return v } ================================================ FILE: gtpv0/ie/private-extension.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewPrivateExtension creates a new PrivateExtension IE from string. func NewPrivateExtension(id uint16, val []byte) *IE { i := New(PrivateExtension, make([]byte, 2+len(val))) binary.BigEndian.PutUint16(i.Payload[:2], id) copy(i.Payload[2:], val) return i } // PrivateExtension returns PrivateExtension value if type matches. func (i *IE) PrivateExtension() ([]byte, error) { if i.Type != PrivateExtension { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustPrivateExtension returns PrivateExtension in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPrivateExtension() []byte { v, _ := i.PrivateExtension() return v } // ExtensionIdentifier returns ExtensionIdentifier value in uint16 if type matches. func (i *IE) ExtensionIdentifier() (uint16, error) { if i.Type != PrivateExtension { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[:2]), nil } // MustExtensionIdentifier returns ExtensionIdentifier in uint16 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustExtensionIdentifier() uint16 { v, _ := i.ExtensionIdentifier() return v } // ExtensionValue returns ExtensionValue value if type matches. func (i *IE) ExtensionValue() ([]byte, error) { if i.Type != PrivateExtension { return nil, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 3 { return nil, io.ErrUnexpectedEOF } return i.Payload[2:], nil } // MustExtensionValue returns ExtensionValue in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustExtensionValue() []byte { v, _ := i.ExtensionValue() return v } ================================================ FILE: gtpv0/ie/qos-profile.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewQualityOfServiceProfile creates a new QualityOfServiceProfile IE. func NewQualityOfServiceProfile(delay, reliability, peak, precedence, mean uint8) *IE { i := New(QualityOfServiceProfile, make([]byte, 3)) i.Payload[0] = ((delay & 0x07) << 3) | (reliability & 0x07) i.Payload[1] = ((peak & 0x0f) << 4) | (precedence & 0x07) i.Payload[2] = mean & 0x1f return i } // QualityOfServiceProfile returns QualityOfServiceProfile if type matches. func (i *IE) QualityOfServiceProfile() ([]byte, error) { if i.Type != QualityOfServiceProfile { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustQualityOfServiceProfile returns QualityOfServiceProfile in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustQualityOfServiceProfile() []byte { v, _ := i.QualityOfServiceProfile() return v } // QoSDelay returns QoS Delay value in uint8 if type matches. func (i *IE) QoSDelay() (uint8, error) { if i.Type != QualityOfServiceProfile { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0] & 0x38, nil } // MustQoSDelay returns QoSDelay in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustQoSDelay() uint8 { v, _ := i.QoSDelay() return v } // QoSReliability returns QoS Reliability value in uint8 if type matches. func (i *IE) QoSReliability() (uint8, error) { if i.Type != QualityOfServiceProfile { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0] & 0x07, nil } // MustQoSReliability returns QoSReliability in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustQoSReliability() uint8 { v, _ := i.QoSReliability() return v } // QoSPeak returns QoS Peak value in uint8 if type matches. func (i *IE) QoSPeak() (uint8, error) { if i.Type != QualityOfServiceProfile { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return i.Payload[1] & 0xf0, nil } // MustQoSPeak returns QoSPeak in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustQoSPeak() uint8 { v, _ := i.QoSPeak() return v } // QoSPrecedence returns QoS Precedence value in uint8 if type matches. func (i *IE) QoSPrecedence() (uint8, error) { if i.Type != QualityOfServiceProfile { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return i.Payload[1] & 0x07, nil } // MustQoSPrecedence returns QoSPrecedence in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustQoSPrecedence() uint8 { v, _ := i.QoSPrecedence() return v } // QoSMean returns QoS Mean value in uint8 if type matches. func (i *IE) QoSMean() (uint8, error) { if i.Type != QualityOfServiceProfile { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 3 { return 0, io.ErrUnexpectedEOF } return i.Payload[2] & 0x0f, nil } // MustQoSMean returns QoSMean in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustQoSMean() uint8 { v, _ := i.QoSMean() return v } ================================================ FILE: gtpv0/ie/rai.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "github.com/wmnsk/go-gtp/utils" ) // NewRouteingAreaIdentity creates a new RouteingAreaIdentity IE. func NewRouteingAreaIdentity(mcc, mnc string, lac uint16, rac uint8) *IE { plmn, err := utils.EncodePLMN(mcc, mnc) if err != nil { return nil } rai := New( RouteingAreaIdentity, make([]byte, 6), ) copy(rai.Payload[0:3], plmn) binary.BigEndian.PutUint16(rai.Payload[3:5], lac) rai.Payload[5] = rac return rai } // RouteingAreaIdentity returns RouteingAreaIdentity value if type matches. func (i *IE) RouteingAreaIdentity() ([]byte, error) { if i.Type != RouteingAreaIdentity { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustRouteingAreaIdentity returns RouteingAreaIdentity in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustRouteingAreaIdentity() []byte { v, _ := i.RouteingAreaIdentity() return v } // MCC returns MCC value if type matches. func (i *IE) MCC() (string, error) { switch i.Type { case RouteingAreaIdentity: if len(i.Payload) < 2 { return "", io.ErrUnexpectedEOF } return utils.SwappedBytesToStr(i.Payload[0:2], false), nil default: return "", &InvalidTypeError{Type: i.Type} } } // MustMCC returns MCC in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustMCC() string { v, _ := i.MCC() return v } // MNC returns MNC value if type matches. func (i *IE) MNC() (string, error) { switch i.Type { case RouteingAreaIdentity: if len(i.Payload) < 2 { return "", io.ErrUnexpectedEOF } return utils.SwappedBytesToStr(i.Payload[1:2], true), nil default: return "", &InvalidTypeError{Type: i.Type} } } // MustMNC returns MNC in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustMNC() string { v, _ := i.MNC() return v } // LAC returns LAC value if type matches. func (i *IE) LAC() (uint16, error) { switch i.Type { case RouteingAreaIdentity: if len(i.Payload) < 5 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[3:5]), nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustLAC returns LAC in uint16 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustLAC() uint16 { v, _ := i.LAC() return v } // RAC returns RAC value if type matches. func (i *IE) RAC() (uint8, error) { switch i.Type { case RouteingAreaIdentity: if len(i.Payload) < 6 { return 0, io.ErrUnexpectedEOF } return i.Payload[5], nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustRAC returns RAC in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustRAC() uint8 { v, _ := i.RAC() return v } ================================================ FILE: gtpv0/ie/recovery.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewRecovery creates a new Recovery IE. func NewRecovery(recovery uint8) *IE { return newUint8ValIE(Recovery, recovery) } // Recovery returns Recovery value if type matches. func (i *IE) Recovery() (uint8, error) { if i.Type != Recovery { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustRecovery returns Recovery in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustRecovery() uint8 { v, _ := i.Recovery() return v } ================================================ FILE: gtpv0/ie/reordering-required.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewReorderingRequired creates a new ReorderingRequired IE. func NewReorderingRequired(required bool) *IE { if required { return New(ReorderingRequired, []byte{0xff}) } return New(ReorderingRequired, []byte{0xfe}) } // ReorderingRequired returns ReorderingRequired value in bool if type matches. func (i *IE) ReorderingRequired() bool { if i.Type != ReorderingRequired { return false } if len(i.Payload) == 0 { return false } return i.Payload[0]&0x01 == 1 } ================================================ FILE: gtpv0/ie/selection-mode.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewSelectionMode creates a new SelectionMode IE. // Note that exactly one of the parameters should be set to true. // Otherwise, you'll get the unexpected result. func NewSelectionMode(mode uint8) *IE { return newUint8ValIE(SelectionMode, mode) } // SelectionMode returns SelectionMode value if type matches. func (i *IE) SelectionMode() (uint8, error) { if i.Type != SelectionMode { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustSelectionMode returns SelectionMode in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustSelectionMode() uint8 { v, _ := i.SelectionMode() return v } ================================================ FILE: gtpv0/ie/tlli.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewTemporaryLogicalLinkIdentity creates a new TemporaryLogicalLinkIdentity IE. func NewTemporaryLogicalLinkIdentity(tlli uint32) *IE { return newUint32ValIE(TemporaryLogicalLinkIdentity, tlli) } // TemporaryLogicalLinkIdentity returns TemporaryLogicalLinkIdentity value in uint32 if type matches. func (i *IE) TemporaryLogicalLinkIdentity() (uint32, error) { if i.Type != TemporaryLogicalLinkIdentity { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload), nil } // MustTemporaryLogicalLinkIdentity returns TemporaryLogicalLinkIdentity in uint32 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustTemporaryLogicalLinkIdentity() uint32 { v, _ := i.TemporaryLogicalLinkIdentity() return v } ================================================ FILE: gtpv0/message/create-pdp-context-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv0/ie" ) // CreatePDPContextRequest is a CreatePDPContextRequest Header and its IEs above. type CreatePDPContextRequest struct { *Header RAI *ie.IE QoSProfile *ie.IE Recovery *ie.IE SelectionMode *ie.IE FlowLabelDataI *ie.IE FlowLabelSignalling *ie.IE EndUserAddress *ie.IE APN *ie.IE PCO *ie.IE SGSNAddressForSignalling *ie.IE SGSNAddressForUserTraffic *ie.IE MSISDN *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewCreatePDPContextRequest creates a new CreatePDPContextRequest. func NewCreatePDPContextRequest(seq, label uint16, tid uint64, ies ...*ie.IE) *CreatePDPContextRequest { c := &CreatePDPContextRequest{ Header: NewHeader( 0x1e, MsgTypeCreatePDPContextRequest, seq, label, tid, nil, ), } // Optional IEs and Private Extensions, or any arbitrary IE. for _, i := range ies { if i == nil { continue } switch i.Type { case ie.RouteingAreaIdentity: c.RAI = i case ie.QualityOfServiceProfile: c.QoSProfile = i case ie.Recovery: c.Recovery = i case ie.SelectionMode: c.SelectionMode = i case ie.FlowLabelDataI: c.FlowLabelDataI = i case ie.FlowLabelSignalling: c.FlowLabelSignalling = i case ie.EndUserAddress: c.EndUserAddress = i case ie.AccessPointName: c.APN = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.GSNAddress: if c.SGSNAddressForSignalling == nil { c.SGSNAddressForSignalling = i } else { c.SGSNAddressForUserTraffic = i } case ie.MSISDN: c.MSISDN = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal returns the byte sequence generated from a CreatePDPContextRequest. func (c *CreatePDPContextRequest) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (c *CreatePDPContextRequest) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.RAI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.QoSProfile; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Recovery; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SelectionMode; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.FlowLabelDataI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.FlowLabelSignalling; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EndUserAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNAddressForSignalling; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MSISDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseCreatePDPContextRequest parses a given byte sequence as a CreatePDPContextRequest. func ParseCreatePDPContextRequest(b []byte) (*CreatePDPContextRequest, error) { c := &CreatePDPContextRequest{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary parses a given byte sequence as a CreatePDPContextRequest. func (c *CreatePDPContextRequest) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.RouteingAreaIdentity: c.RAI = i case ie.QualityOfServiceProfile: c.QoSProfile = i case ie.Recovery: c.Recovery = i case ie.SelectionMode: c.SelectionMode = i case ie.FlowLabelDataI: c.FlowLabelDataI = i case ie.FlowLabelSignalling: c.FlowLabelSignalling = i case ie.EndUserAddress: c.EndUserAddress = i case ie.AccessPointName: c.APN = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.GSNAddress: if c.SGSNAddressForSignalling == nil { c.SGSNAddressForSignalling = i } else { c.SGSNAddressForUserTraffic = i } case ie.MSISDN: c.MSISDN = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (c *CreatePDPContextRequest) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.RAI; ie != nil { l += ie.MarshalLen() } if ie := c.QoSProfile; ie != nil { l += ie.MarshalLen() } if ie := c.Recovery; ie != nil { l += ie.MarshalLen() } if ie := c.SelectionMode; ie != nil { l += ie.MarshalLen() } if ie := c.FlowLabelDataI; ie != nil { l += ie.MarshalLen() } if ie := c.FlowLabelSignalling; ie != nil { l += ie.MarshalLen() } if ie := c.EndUserAddress; ie != nil { l += ie.MarshalLen() } if ie := c.APN; ie != nil { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } if ie := c.SGSNAddressForSignalling; ie != nil { l += ie.MarshalLen() } if ie := c.SGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := c.MSISDN; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *CreatePDPContextRequest) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 20) } // MessageTypeName returns the name of protocol. func (c *CreatePDPContextRequest) MessageTypeName() string { return "Create PDP Context Request" } // TID returns the TID in human-readable string. func (c *CreatePDPContextRequest) TID() string { return c.tid() } ================================================ FILE: gtpv0/message/create-pdp-context-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes CreatePDPContextRequest into bytes. // // Deprecated: use CreatePDPContextRequest.Marshal instead. func (c *CreatePDPContextRequest) Serialize() ([]byte, error) { log.Println("CreatePDPContextRequest.Serialize is deprecated. use CreatePDPContextRequest.Marshal instead") return c.Marshal() } // SerializeTo serializes CreatePDPContextRequest into bytes given as b. // // Deprecated: use CreatePDPContextRequest.MarshalTo instead. func (c *CreatePDPContextRequest) SerializeTo(b []byte) error { log.Println("CreatePDPContextRequest.SerializeTo is deprecated. use CreatePDPContextRequest.MarshalTo instead") return c.MarshalTo(b) } // DecodeCreatePDPContextRequest decodes bytes as CreatePDPContextRequest. // // Deprecated: use ParseCreatePDPContextRequest instead. func DecodeCreatePDPContextRequest(b []byte) (*CreatePDPContextRequest, error) { log.Println("DecodeCreatePDPContextRequest is deprecated. use ParseCreatePDPContextRequest instead") return ParseCreatePDPContextRequest(b) } // DecodeFromBytes decodes bytes as CreatePDPContextRequest. // // Deprecated: use CreatePDPContextRequest.UnmarshalBinary instead. func (c *CreatePDPContextRequest) DecodeFromBytes(b []byte) error { log.Println("CreatePDPContextRequest.DecodeFromBytes is deprecated. use CreatePDPContextRequest.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of CreatePDPContextRequest. // // Deprecated: use CreatePDPContextRequest.MarshalLen instead. func (c *CreatePDPContextRequest) Len() int { log.Println("CreatePDPContextRequest.Len is deprecated. use CreatePDPContextRequest.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv0/message/create-pdp-context-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0" "github.com/wmnsk/go-gtp/gtpv0/ie" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestCreatePDPContextRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "mandatory-only", Structured: message.NewCreatePDPContextRequest( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ie.NewQualityOfServiceProfile(1, 1, 1, 1, 1), ie.NewSelectionMode(gtpv0.SelectionModeMSorNetworkProvidedAPNSubscribedVerified), ie.NewFlowLabelDataI(11), ie.NewFlowLabelSignalling(22), ie.NewEndUserAddress("1.1.1.1"), ie.NewAccessPointName("some.apn.example"), ie.NewGSNAddress("2.2.2.2"), ie.NewGSNAddress("3.3.3.3"), ie.NewMSISDN("819012345678"), ), Serialized: []byte{ // Header 0x1e, 0x10, 0x00, 0x41, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // QualityOfServiceProfile 0x06, 0x09, 0x11, 0x01, // SelectionMode 0x0f, 0xf0, // FlowLabelDataI 0x10, 0x00, 0x0b, // FlowLabelSignalling 0x11, 0x00, 0x16, // EndUserAddress 0x80, 0x00, 0x06, 0xf1, 0x21, 0x01, 0x01, 0x01, 0x01, // AccessPointName 0x83, 0x00, 0x11, 0x04, 0x73, 0x6f, 0x6d, 0x65, 0x03, 0x61, 0x70, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, // SGSNAddressForSignalling 0x85, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, // SGSNAddressForUserData 0x85, 0x00, 0x04, 0x03, 0x03, 0x03, 0x03, // MSISDN 0x86, 0x00, 0x07, 0x91, 0x18, 0x09, 0x21, 0x43, 0x65, 0x87, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseCreatePDPContextRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv0/message/create-pdp-context-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv0/ie" ) // CreatePDPContextResponse is a CreatePDPContextResponse Header and its IEs above. type CreatePDPContextResponse struct { *Header Cause *ie.IE QoSProfile *ie.IE ReorderingRequired *ie.IE Recovery *ie.IE FlowLabelDataI *ie.IE FlowLabelSignalling *ie.IE ChargingID *ie.IE EndUserAddress *ie.IE PCO *ie.IE GGSNAddressForSignalling *ie.IE GGSNAddressForUserTraffic *ie.IE ChargingGatewayAddress *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewCreatePDPContextResponse creates a new CreatePDPContextResponse. func NewCreatePDPContextResponse(seq, label uint16, tid uint64, ies ...*ie.IE) *CreatePDPContextResponse { c := &CreatePDPContextResponse{ Header: NewHeader( 0x1e, MsgTypeCreatePDPContextResponse, seq, label, tid, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.QualityOfServiceProfile: c.QoSProfile = i case ie.ReorderingRequired: c.ReorderingRequired = i case ie.Recovery: c.Recovery = i case ie.FlowLabelDataI: c.FlowLabelDataI = i case ie.FlowLabelSignalling: c.FlowLabelSignalling = i case ie.ChargingID: c.ChargingID = i case ie.EndUserAddress: c.EndUserAddress = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.GSNAddress: if c.GGSNAddressForSignalling == nil { c.GGSNAddressForSignalling = i } else { c.GGSNAddressForUserTraffic = i } case ie.ChargingGatewayAddress: c.ChargingGatewayAddress = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal returns the byte sequence generated from a CreatePDPContextResponse. func (c *CreatePDPContextResponse) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (c *CreatePDPContextResponse) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.QoSProfile; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ReorderingRequired; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Recovery; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.FlowLabelDataI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.FlowLabelSignalling; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChargingID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EndUserAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.GGSNAddressForSignalling; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.GGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChargingGatewayAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseCreatePDPContextResponse parses a given byte sequence as a CreatePDPContextResponse. func ParseCreatePDPContextResponse(b []byte) (*CreatePDPContextResponse, error) { c := &CreatePDPContextResponse{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary parses a given byte sequence as a CreatePDPContextResponse. func (c *CreatePDPContextResponse) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.QualityOfServiceProfile: c.QoSProfile = i case ie.ReorderingRequired: c.ReorderingRequired = i case ie.Recovery: c.Recovery = i case ie.FlowLabelDataI: c.FlowLabelDataI = i case ie.FlowLabelSignalling: c.FlowLabelSignalling = i case ie.ChargingID: c.ChargingID = i case ie.EndUserAddress: c.EndUserAddress = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.GSNAddress: if c.GGSNAddressForSignalling == nil { c.GGSNAddressForSignalling = i } else { c.GGSNAddressForUserTraffic = i } case ie.ChargingGatewayAddress: c.ChargingGatewayAddress = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (c *CreatePDPContextResponse) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.Cause; ie != nil { l += ie.MarshalLen() } if ie := c.QoSProfile; ie != nil { l += ie.MarshalLen() } if ie := c.ReorderingRequired; ie != nil { l += ie.MarshalLen() } if ie := c.Recovery; ie != nil { l += ie.MarshalLen() } if ie := c.FlowLabelDataI; ie != nil { l += ie.MarshalLen() } if ie := c.FlowLabelSignalling; ie != nil { l += ie.MarshalLen() } if ie := c.ChargingID; ie != nil { l += ie.MarshalLen() } if ie := c.EndUserAddress; ie != nil { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } if ie := c.GGSNAddressForSignalling; ie != nil { l += ie.MarshalLen() } if ie := c.GGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := c.ChargingGatewayAddress; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *CreatePDPContextResponse) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 20) } // MessageTypeName returns the name of protocol. func (c *CreatePDPContextResponse) MessageTypeName() string { return "Create PDP Context Response" } // TID returns the TID in human-readable string. func (c *CreatePDPContextResponse) TID() string { return c.tid() } ================================================ FILE: gtpv0/message/create-pdp-context-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes CreatePDPContextResponse into bytes. // // Deprecated: use CreatePDPContextResponse.Marshal instead. func (c *CreatePDPContextResponse) Serialize() ([]byte, error) { log.Println("CreatePDPContextResponse.Serialize is deprecated. use CreatePDPContextResponse.Marshal instead") return c.Marshal() } // SerializeTo serializes CreatePDPContextResponse into bytes given as b. // // Deprecated: use CreatePDPContextResponse.MarshalTo instead. func (c *CreatePDPContextResponse) SerializeTo(b []byte) error { log.Println("CreatePDPContextResponse.SerializeTo is deprecated. use CreatePDPContextResponse.MarshalTo instead") return c.MarshalTo(b) } // DecodeCreatePDPContextResponse decodes bytes as CreatePDPContextResponse. // // Deprecated: use ParseCreatePDPContextResponse instead. func DecodeCreatePDPContextResponse(b []byte) (*CreatePDPContextResponse, error) { log.Println("DecodeCreatePDPContextResponse is deprecated. use ParseCreatePDPContextResponse instead") return ParseCreatePDPContextResponse(b) } // DecodeFromBytes decodes bytes as CreatePDPContextResponse. // // Deprecated: use CreatePDPContextResponse.UnmarshalBinary instead. func (c *CreatePDPContextResponse) DecodeFromBytes(b []byte) error { log.Println("CreatePDPContextResponse.DecodeFromBytes is deprecated. use CreatePDPContextResponse.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of CreatePDPContextResponse. // // Deprecated: use CreatePDPContextResponse.MarshalLen instead. func (c *CreatePDPContextResponse) Len() int { log.Println("CreatePDPContextResponse.Len is deprecated. use CreatePDPContextResponse.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv0/message/create-pdp-context-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0" "github.com/wmnsk/go-gtp/gtpv0/ie" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestCreatePDPContextResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "request-accepted", Structured: message.NewCreatePDPContextResponse( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ie.NewCause(gtpv0.CauseRequestAccepted), ie.NewQualityOfServiceProfile(1, 1, 1, 1, 1), ie.NewReorderingRequired(false), ie.NewFlowLabelDataI(11), ie.NewFlowLabelSignalling(22), ie.NewChargingID(0xff), ie.NewEndUserAddress("1.1.1.1"), ie.NewGSNAddress("2.2.2.2"), ie.NewGSNAddress("3.3.3.3"), ), Serialized: []byte{ // Header 0x1e, 0x11, 0x00, 0x2a, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // Cause 0x01, 0x80, // QualityOfServiceProfile 0x06, 0x09, 0x11, 0x01, // ReorderingRequired 0x08, 0xfe, // FlowLabelDataI 0x10, 0x00, 0x0b, // FlowLabelSignalling 0x11, 0x00, 0x16, // ChargingID 0x7f, 0x00, 0x00, 0x00, 0xff, // EndUserAddress 0x80, 0x00, 0x06, 0xf1, 0x21, 0x01, 0x01, 0x01, 0x01, // SGSNAddressForSignalling 0x85, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, // SGSNAddressForUserData 0x85, 0x00, 0x04, 0x03, 0x03, 0x03, 0x03, }, }, { Description: "no-resources", Structured: message.NewCreatePDPContextResponse( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ie.NewCause(gtpv0.CauseNoResourcesAvailable), ), Serialized: []byte{ // Header 0x1e, 0x11, 0x00, 0x02, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // Cause 0x01, 0xc7, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseCreatePDPContextResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv0/message/delete-pdp-context-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE fild. package message import ( "fmt" "github.com/wmnsk/go-gtp/gtpv0/ie" ) // DeletePDPContextRequest is a DeletePDPContextRequest Header and its AdditionalIEs abovd. type DeletePDPContextRequest struct { *Header PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeletePDPContextRequest creates a new DeletePDPContextRequest. func NewDeletePDPContextRequest(seq, label uint16, tid uint64, ies ...*ie.IE) *DeletePDPContextRequest { d := &DeletePDPContextRequest{ Header: NewHeader( 0x1e, MsgTypeDeletePDPContextRequest, seq, label, tid, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal returns the byte sequence generated from a DeletePDPContextRequest. func (d *DeletePDPContextRequest) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (d *DeletePDPContextRequest) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeletePDPContextRequest parses a given byte sequence as a DeletePDPContextRequest. func ParseDeletePDPContextRequest(b []byte) (*DeletePDPContextRequest, error) { d := &DeletePDPContextRequest{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary parses a given byte sequence as a DeletePDPContextRequest. func (d *DeletePDPContextRequest) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return fmt.Errorf("failed to Parse Header: %w", err) } if len(d.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (d *DeletePDPContextRequest) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeletePDPContextRequest) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 20) } // MessageTypeName returns the name of protocol. func (d *DeletePDPContextRequest) MessageTypeName() string { return "Delete PDP Context Request" } // TID returns the TID in human-readable string. func (d *DeletePDPContextRequest) TID() string { return d.tid() } ================================================ FILE: gtpv0/message/delete-pdp-context-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes DeletePDPContextRequest into bytes. // // Deprecated: use DeletePDPContextRequest.Marshal instead. func (d *DeletePDPContextRequest) Serialize() ([]byte, error) { log.Println("DeletePDPContextRequest.Serialize is deprecated. use DeletePDPContextRequest.Marshal instead") return d.Marshal() } // SerializeTo serializes DeletePDPContextRequest into bytes given as b. // // Deprecated: use DeletePDPContextRequest.MarshalTo instead. func (d *DeletePDPContextRequest) SerializeTo(b []byte) error { log.Println("DeletePDPContextRequest.SerializeTo is deprecated. use DeletePDPContextRequest.MarshalTo instead") return d.MarshalTo(b) } // DecodeDeletePDPContextRequest decodes bytes as DeletePDPContextRequest. // // Deprecated: use ParseDeletePDPContextRequest instead. func DecodeDeletePDPContextRequest(b []byte) (*DeletePDPContextRequest, error) { log.Println("DecodeDeletePDPContextRequest is deprecated. use ParseDeletePDPContextRequest instead") return ParseDeletePDPContextRequest(b) } // DecodeFromBytes decodes bytes as DeletePDPContextRequest. // // Deprecated: use DeletePDPContextRequest.UnmarshalBinary instead. func (d *DeletePDPContextRequest) DecodeFromBytes(b []byte) error { log.Println("DeletePDPContextRequest.DecodeFromBytes is deprecated. use DeletePDPContextRequest.UnmarshalBinary instead") return d.UnmarshalBinary(b) } // Len returns the actual length of DeletePDPContextRequest. // // Deprecated: use DeletePDPContextRequest.MarshalLen instead. func (d *DeletePDPContextRequest) Len() int { log.Println("DeletePDPContextRequest.Len is deprecated. use DeletePDPContextRequest.MarshalLen instead") return d.MarshalLen() } ================================================ FILE: gtpv0/message/delete-pdp-context-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestDeletePDPContextRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "normal", Structured: message.NewDeletePDPContextRequest( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ), Serialized: []byte{ // Header 0x1e, 0x14, 0x00, 0x00, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeletePDPContextRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv0/message/delete-pdp-context-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE fild. package message import ( "github.com/wmnsk/go-gtp/gtpv0/ie" ) // DeletePDPContextResponse is a DeletePDPContextResponse Header and its AdditionalIEs abovd. type DeletePDPContextResponse struct { *Header Cause *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeletePDPContextResponse creates a new DeletePDPContextResponse. func NewDeletePDPContextResponse(seq, label uint16, tid uint64, ies ...*ie.IE) *DeletePDPContextResponse { d := &DeletePDPContextResponse{ Header: NewHeader( 0x1e, MsgTypeDeletePDPContextResponse, seq, label, tid, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal returns the byte sequence generated from a DeletePDPContextResponsd. func (d *DeletePDPContextResponse) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (d *DeletePDPContextResponse) MarshalTo(b []byte) error { // XXX - add validation! d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeletePDPContextResponse parses a given byte sequence as a DeletePDPContextResponsd. func ParseDeletePDPContextResponse(b []byte) (*DeletePDPContextResponse, error) { d := &DeletePDPContextResponse{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary parses a given byte sequence as a DeletePDPContextResponsd. func (d *DeletePDPContextResponse) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (d *DeletePDPContextResponse) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeletePDPContextResponse) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 20) } // MessageTypeName returns the name of protocol. func (d *DeletePDPContextResponse) MessageTypeName() string { return "Delete PDP Context Response" } // TID returns the TID in human-readable string. func (d *DeletePDPContextResponse) TID() string { return d.tid() } ================================================ FILE: gtpv0/message/delete-pdp-context-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes DeletePDPContextResponse into bytes. // // Deprecated: use DeletePDPContextResponse.Marshal instead. func (d *DeletePDPContextResponse) Serialize() ([]byte, error) { log.Println("DeletePDPContextResponse.Serialize is deprecated. use DeletePDPContextResponse.Marshal instead") return d.Marshal() } // SerializeTo serializes DeletePDPContextResponse into bytes given as b. // // Deprecated: use DeletePDPContextResponse.MarshalTo instead. func (d *DeletePDPContextResponse) SerializeTo(b []byte) error { log.Println("DeletePDPContextResponse.SerializeTo is deprecated. use DeletePDPContextResponse.MarshalTo instead") return d.MarshalTo(b) } // DecodeDeletePDPContextResponse decodes bytes as DeletePDPContextResponse. // // Deprecated: use ParseDeletePDPContextResponse instead. func DecodeDeletePDPContextResponse(b []byte) (*DeletePDPContextResponse, error) { log.Println("DecodeDeletePDPContextResponse is deprecated. use ParseDeletePDPContextResponse instead") return ParseDeletePDPContextResponse(b) } // DecodeFromBytes decodes bytes as DeletePDPContextResponse. // // Deprecated: use DeletePDPContextResponse.UnmarshalBinary instead. func (d *DeletePDPContextResponse) DecodeFromBytes(b []byte) error { log.Println("DeletePDPContextResponse.DecodeFromBytes is deprecated. use DeletePDPContextResponse.UnmarshalBinary instead") return d.UnmarshalBinary(b) } // Len returns the actual length of DeletePDPContextResponse. // // Deprecated: use DeletePDPContextResponse.MarshalLen instead. func (d *DeletePDPContextResponse) Len() int { log.Println("DeletePDPContextResponse.Len is deprecated. use DeletePDPContextResponse.MarshalLen instead") return d.MarshalLen() } ================================================ FILE: gtpv0/message/delete-pdp-context-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0" "github.com/wmnsk/go-gtp/gtpv0/ie" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestDeletePDPContextResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "request-accepted", Structured: message.NewDeletePDPContextResponse( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ie.NewCause(gtpv0.CauseRequestAccepted), ), Serialized: []byte{ // Hewader 0x1e, 0x15, 0x00, 0x02, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // Cause 0x01, 0x80, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeletePDPContextResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv0/message/echo-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "fmt" "github.com/wmnsk/go-gtp/gtpv0/ie" ) // EchoRequest is a EchoRequest Header and its AdditionalIEs above. type EchoRequest struct { *Header PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewEchoRequest creates a new EchoRequest. func NewEchoRequest(seq, label uint16, tid uint64, ies ...*ie.IE) *EchoRequest { e := &EchoRequest{ Header: NewHeader( 0x1e, MsgTypeEchoRequest, seq, label, tid, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } e.SetLength() return e } // Marshal returns the byte sequence generated from a EchoRequest. func (e *EchoRequest) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *EchoRequest) MarshalTo(b []byte) error { if e.Header.Payload != nil { e.Header.Payload = nil } e.Header.Payload = make([]byte, e.MarshalLen()-e.Header.MarshalLen()) offset := 0 if ie := e.PrivateExtension; ie != nil { if err := ie.MarshalTo(e.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } e.Header.SetLength() return e.Header.MarshalTo(b) } // ParseEchoRequest parses a given byte sequence as a EchoRequest. func ParseEchoRequest(b []byte) (*EchoRequest, error) { e := &EchoRequest{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary parses a given byte sequence as a EchoRequest. func (e *EchoRequest) UnmarshalBinary(b []byte) error { var err error e.Header, err = ParseHeader(b) if err != nil { return fmt.Errorf("failed to Parse Header: %w", err) } if len(e.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(e.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (e *EchoRequest) MarshalLen() int { l := e.Header.MarshalLen() - len(e.Header.Payload) if ie := e.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (e *EchoRequest) SetLength() { e.Header.Length = uint16(e.MarshalLen() - 20) } // MessageTypeName returns the name of protocol. func (e *EchoRequest) MessageTypeName() string { return "Echo Request" } // TID returns the TID in human-readable string. func (e *EchoRequest) TID() string { return e.tid() } ================================================ FILE: gtpv0/message/echo-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes EchoRequest into bytes. // // Deprecated: use EchoRequest.Marshal instead. func (e *EchoRequest) Serialize() ([]byte, error) { log.Println("EchoRequest.Serialize is deprecated. use EchoRequest.Marshal instead") return e.Marshal() } // SerializeTo serializes EchoRequest into bytes given as b. // // Deprecated: use EchoRequest.MarshalTo instead. func (e *EchoRequest) SerializeTo(b []byte) error { log.Println("EchoRequest.SerializeTo is deprecated. use EchoRequest.MarshalTo instead") return e.MarshalTo(b) } // DecodeEchoRequest decodes bytes as EchoRequest. // // Deprecated: use ParseEchoRequest instead. func DecodeEchoRequest(b []byte) (*EchoRequest, error) { log.Println("DecodeEchoRequest is deprecated. use ParseEchoRequest instead") return ParseEchoRequest(b) } // DecodeFromBytes decodes bytes as EchoRequest. // // Deprecated: use EchoRequest.UnmarshalBinary instead. func (e *EchoRequest) DecodeFromBytes(b []byte) error { log.Println("EchoRequest.DecodeFromBytes is deprecated. use EchoRequest.UnmarshalBinary instead") return e.UnmarshalBinary(b) } // Len returns the actual length of EchoRequest. // // Deprecated: use EchoRequest.MarshalLen instead. func (e *EchoRequest) Len() int { log.Println("EchoRequest.Len is deprecated. use EchoRequest.MarshalLen instead") return e.MarshalLen() } ================================================ FILE: gtpv0/message/echo-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestEchoRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "normal", Structured: message.NewEchoRequest( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ), Serialized: []byte{ // Header 0x1e, 0x01, 0x00, 0x00, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseEchoRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv0/message/echo-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv0/ie" ) // EchoResponse is a EchoResponse Header and its AdditionalIEs above. type EchoResponse struct { *Header Recovery *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewEchoResponse creates a new GTP. func NewEchoResponse(seq, label uint16, tid uint64, ies ...*ie.IE) *EchoResponse { e := &EchoResponse{ Header: NewHeader( 0x1e, MsgTypeEchoResponse, seq, label, tid, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Recovery: e.Recovery = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } e.SetLength() return e } // Marshal returns the byte sequence generated from a EchoResponse. func (e *EchoResponse) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *EchoResponse) MarshalTo(b []byte) error { if e.Header.Payload != nil { e.Header.Payload = nil } e.Header.Payload = make([]byte, e.MarshalLen()-e.Header.MarshalLen()) offset := 0 if ie := e.Recovery; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } e.Header.SetLength() return e.Header.MarshalTo(b) } // ParseEchoResponse parses a given byte sequence as a EchoResponse. func ParseEchoResponse(b []byte) (*EchoResponse, error) { e := &EchoResponse{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary parses a given byte sequence as a EchoResponse. func (e *EchoResponse) UnmarshalBinary(b []byte) error { var err error e.Header, err = ParseHeader(b) if err != nil { return err } if len(e.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(e.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Recovery: e.Recovery = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (e *EchoResponse) MarshalLen() int { l := e.Header.MarshalLen() - len(e.Header.Payload) if ie := e.Recovery; ie != nil { l += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (e *EchoResponse) SetLength() { e.Header.Length = uint16(e.MarshalLen() - 20) } // MessageTypeName returns the name of protocol. func (e *EchoResponse) MessageTypeName() string { return "Echo Response" } // TID returns the TID in human-readable string. func (e *EchoResponse) TID() string { return e.tid() } ================================================ FILE: gtpv0/message/echo-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes EchoResponse into bytes. // // Deprecated: use EchoResponse.Marshal instead. func (e *EchoResponse) Serialize() ([]byte, error) { log.Println("EchoResponse.Serialize is deprecated. use EchoResponse.Marshal instead") return e.Marshal() } // SerializeTo serializes EchoResponse into bytes given as b. // // Deprecated: use EchoResponse.MarshalTo instead. func (e *EchoResponse) SerializeTo(b []byte) error { log.Println("EchoResponse.SerializeTo is deprecated. use EchoResponse.MarshalTo instead") return e.MarshalTo(b) } // DecodeEchoResponse decodes bytes as EchoResponse. // // Deprecated: use ParseEchoResponse instead. func DecodeEchoResponse(b []byte) (*EchoResponse, error) { log.Println("DecodeEchoResponse is deprecated. use ParseEchoResponse instead") return ParseEchoResponse(b) } // DecodeFromBytes decodes bytes as EchoResponse. // // Deprecated: use EchoResponse.UnmarshalBinary instead. func (e *EchoResponse) DecodeFromBytes(b []byte) error { log.Println("EchoResponse.DecodeFromBytes is deprecated. use EchoResponse.UnmarshalBinary instead") return e.UnmarshalBinary(b) } // Len returns the actual length of EchoResponse. // // Deprecated: use EchoResponse.MarshalLen instead. func (e *EchoResponse) Len() int { log.Println("EchoResponse.Len is deprecated. use EchoResponse.MarshalLen instead") return e.MarshalLen() } ================================================ FILE: gtpv0/message/echo-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0/ie" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestEchoResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "with-recovery", Structured: message.NewEchoResponse( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ie.NewRecovery(0x80), ), Serialized: []byte{ // Hewader 0x1e, 0x02, 0x00, 0x02, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // Recovery 0x0e, 0x80, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseEchoResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv0/message/errors.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "errors" // Error definitions. var ( ErrInvalidLength = errors.New("got invalid length") ErrTooShortToMarshal = errors.New("too short to Marshal") ErrTooShortToParse = errors.New("too short to Parse as GTPv0") ) ================================================ FILE: gtpv0/message/generic.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "fmt" "github.com/wmnsk/go-gtp/gtpv0/ie" ) // Generic is a Generic Header and its IEs above. type Generic struct { *Header IEs []*ie.IE } // NewGeneric creates a new GTPv0 Generic. func NewGeneric(msgType uint8, seq, label uint16, tid uint64, ie ...*ie.IE) *Generic { g := &Generic{ Header: NewHeader(0x1e, msgType, seq, label, tid, nil), } for _, i := range ie { if i == nil { continue } g.IEs = append(g.IEs, i) } g.SetLength() return g } // Marshal returns the byte sequence generated from a Generic. func (g *Generic) Marshal() ([]byte, error) { b := make([]byte, g.MarshalLen()) if err := g.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (g *Generic) MarshalTo(b []byte) error { if g.Header.Payload != nil { g.Header.Payload = nil } g.Header.Payload = make([]byte, g.MarshalLen()-g.Header.MarshalLen()) offset := 0 for _, ie := range g.IEs { if ie == nil { continue } if err := ie.MarshalTo(g.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } g.Header.SetLength() return g.Header.MarshalTo(b) } // ParseGeneric parses a given byte sequence as a Generic. func ParseGeneric(b []byte) (*Generic, error) { g := &Generic{} if err := g.UnmarshalBinary(b); err != nil { return nil, err } return g, nil } // UnmarshalBinary parses a given byte sequence as a Generic. func (g *Generic) UnmarshalBinary(b []byte) error { var err error g.Header, err = ParseHeader(b) if err != nil { return err } if len(g.Header.Payload) < 2 { return nil } g.IEs, err = ie.ParseMultiIEs(g.Header.Payload) if err != nil { return err } return nil } // MarshalLen returns the serial length of Data. func (g *Generic) MarshalLen() int { l := g.Header.MarshalLen() - len(g.Header.Payload) for _, ie := range g.IEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (g *Generic) SetLength() { g.Header.Length = uint16(g.MarshalLen() - 20) } // MessageTypeName returns the name of protocol. func (g *Generic) MessageTypeName() string { return fmt.Sprintf("Unknown (%d)", g.Type) } // TID returns the TID in human-readable string. func (g *Generic) TID() string { return g.tid() } // AddIE add IEs to Generic type of GTPv2 message and update Length field. func (g *Generic) AddIE(ie ...*ie.IE) { g.IEs = append(g.IEs, ie...) g.SetLength() } ================================================ FILE: gtpv0/message/generic_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes Generic into bytes. // // Deprecated: use Generic.Marshal instead. func (g *Generic) Serialize() ([]byte, error) { log.Println("Generic.Serialize is deprecated. use Generic.Marshal instead") return g.Marshal() } // SerializeTo serializes Generic into bytes given as b. // // Deprecated: use Generic.MarshalTo instead. func (g *Generic) SerializeTo(b []byte) error { log.Println("Generic.SerializeTo is deprecated. use Generic.MarshalTo instead") return g.MarshalTo(b) } // DecodeGeneric decodes bytes as Generic. // // Deprecated: use ParseGeneric instead. func DecodeGeneric(b []byte) (*Generic, error) { log.Println("DecodeGeneric is deprecated. use ParseGeneric instead") return ParseGeneric(b) } // DecodeFromBytes decodes bytes as Generic. // // Deprecated: use Generic.UnmarshalBinary instead. func (g *Generic) DecodeFromBytes(b []byte) error { log.Println("Generic.DecodeFromBytes is deprecated. use Generic.UnmarshalBinary instead") return g.UnmarshalBinary(b) } // Len returns the actual length of Generic. // // Deprecated: use Generic.MarshalLen instead. func (g *Generic) Len() int { log.Println("Generic.Len is deprecated. use Generic.MarshalLen instead") return g.MarshalLen() } ================================================ FILE: gtpv0/message/generic_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0/ie" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestGeneric(t *testing.T) { cases := []testutils.TestCase{ { Description: "echo-req", Structured: message.NewGeneric( message.MsgTypeEchoRequest, testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ), Serialized: []byte{ // Header 0x1e, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, }, }, { Description: "echo-res", Structured: message.NewGeneric( message.MsgTypeEchoResponse, testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ie.NewRecovery(0x80), ), Serialized: []byte{ // Hewader 0x1e, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // Recovery 0x0e, 0x80, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseGeneric(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv0/message/header.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "encoding/binary" "fmt" "github.com/wmnsk/go-gtp/utils" ) // Header is a GTPv1 common header. type Header struct { Flags uint8 Type uint8 Length uint16 SequenceNumber uint16 SndcpNumber uint8 FlowLabel uint16 TID uint64 Payload []byte } // NewHeader creates a new Header. func NewHeader(flags, mtype uint8, seq, label uint16, tid uint64, payload []byte) *Header { h := &Header{ Flags: flags, Type: mtype, SequenceNumber: seq, FlowLabel: label, SndcpNumber: 0xff, TID: tid, Payload: payload, } h.SetLength() return h } // HeaderFlags returns a Header Flag built by its components given as arguments. func HeaderFlags(v, p, s int) uint8 { return uint8( ((v & 0x7) << 5) | ((p & 0x1) << 4) | (s & 0x1) | 0x0e, ) } // Marshal returns the byte sequence generated from an IE instance. func (h *Header) Marshal() ([]byte, error) { b := make([]byte, h.MarshalLen()) if err := h.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (h *Header) MarshalTo(b []byte) error { if len(b) < h.MarshalLen() { return ErrTooShortToMarshal } b[0] = h.Flags b[1] = h.Type binary.BigEndian.PutUint16(b[2:4], h.Length) binary.BigEndian.PutUint16(b[4:6], h.SequenceNumber) binary.BigEndian.PutUint16(b[6:8], h.FlowLabel) binary.BigEndian.PutUint32(b[8:12], uint32(int(h.SndcpNumber)<<24|0xffffff)) binary.BigEndian.PutUint64(b[12:20], h.TID) // two bytes of padding before payload. copy(b[20:h.MarshalLen()], h.Payload) return nil } // ParseHeader Parses given byte sequence as a GTPv1 header. func ParseHeader(b []byte) (*Header, error) { h := &Header{} if err := h.UnmarshalBinary(b); err != nil { return nil, err } return h, nil } // UnmarshalBinary sets the values retrieved from byte sequence in GTPv1 header. func (h *Header) UnmarshalBinary(b []byte) error { l := len(b) if l < 20 { return ErrTooShortToParse } h.Flags = b[0] h.Type = b[1] h.Length = binary.BigEndian.Uint16(b[2:4]) h.SequenceNumber = binary.BigEndian.Uint16(b[4:6]) h.FlowLabel = binary.BigEndian.Uint16(b[6:8]) h.SndcpNumber = b[9] h.TID = binary.BigEndian.Uint64(b[12:20]) if int(h.Length)+20 != l { h.Payload = b[20:] return nil } h.Payload = b[20 : 20+h.Length] return nil } // MarshalLen returns the serial length of Header. func (h *Header) MarshalLen() int { return 20 + len(h.Payload) } // SetLength sets the length in Length field. func (h *Header) SetLength() { h.Length = uint16(len(h.Payload)) } // String returns the GTPv1 header values in human readable format. func (h *Header) String() string { return fmt.Sprintf("{Flags: %#x, Type: %#x, Length: %d, SequenceNumber: %#04x, FlowLabel: %#04x, SndcpNumber: %#02x, TID: %#016x, Payload: %#v}", h.Flags, h.Type, h.Length, h.SequenceNumber, h.FlowLabel, h.SndcpNumber, h.TID, h.Payload, ) } // tid returns the tid in human-readable string. func (h *Header) tid() string { b := make([]byte, 8) binary.BigEndian.PutUint64(b, h.TID) return utils.SwappedBytesToStr(b, false) } // Version returns the GTP version. func (h *Header) Version() int { return 0 } // MessageType returns the type of message. func (h *Header) MessageType() uint8 { return h.Type } ================================================ FILE: gtpv0/message/header_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes Header into bytes. // // Deprecated: use Header.Marshal instead. func (h *Header) Serialize() ([]byte, error) { log.Println("Header.Serialize is deprecated. use Header.Marshal instead") return h.Marshal() } // SerializeTo serializes Header into bytes given as b. // // Deprecated: use Header.MarshalTo instead. func (h *Header) SerializeTo(b []byte) error { log.Println("Header.SerializeTo is deprecated. use Header.MarshalTo instead") return h.MarshalTo(b) } // DecodeHeader decodes bytes as Header. // // Deprecated: use ParseHeader instead. func DecodeHeader(b []byte) (*Header, error) { log.Println("DecodeHeader is deprecated. use ParseHeader instead") return ParseHeader(b) } // DecodeFromBytes decodes bytes as Header. // // Deprecated: use Header.UnmarshalBinary instead. func (h *Header) DecodeFromBytes(b []byte) error { log.Println("Header.DecodeFromBytes is deprecated. use Header.UnmarshalBinary instead") return h.UnmarshalBinary(b) } // Len returns the actual length of Header. // // Deprecated: use Header.MarshalLen instead. func (h *Header) Len() int { log.Println("Header.Len is deprecated. use Header.MarshalLen instead") return h.MarshalLen() } ================================================ FILE: gtpv0/message/header_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestHeader(t *testing.T) { cases := []testutils.TestCase{ { Description: "normal", Structured: message.NewHeader( message.HeaderFlags( 0, // version 1, // Protocol Type 0, // N-PDU? ), //Flags 0x10, // Message type testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, []byte{ // Payload 0xde, 0xad, 0xbe, 0xef, }, ), Serialized: []byte{ // Flags 0x1e, // MessageType 0x10, // SequenceNumber 0x00, 0x04, 0x00, 0x01, // FlowLabel 0x00, 0x00, // SndcpNumber 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // dummy Payload 0xde, 0xad, 0xbe, 0xef, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseHeader(b) if err != nil { return nil, err } return v, nil }) } ================================================ FILE: gtpv0/message/message.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. /* Package message provides encoding/decoding feature of GTPv0 protocol. */ package message import ( "fmt" "reflect" "strings" "github.com/wmnsk/go-gtp/gtpv0/ie" ) // MessageType definitions. const ( _ uint8 = iota MsgTypeEchoRequest MsgTypeEchoResponse MsgTypeVersionNotSupported MsgTypeNodeAliveRequest MsgTypeNodeAliveResponse MsgTypeRedirectionRequest MsgTypeRedirectionResponse _ _ _ _ _ _ _ _ MsgTypeCreatePDPContextRequest // 16 MsgTypeCreatePDPContextResponse MsgTypeUpdatePDPContextRequest MsgTypeUpdatePDPContextResponse MsgTypeDeletePDPContextRequest MsgTypeDeletePDPContextResponse MsgTypeCreateAAPDPContextRequest MsgTypeCreateAAPDPContextResponse MsgTypeDeleteAAPDPContextRequest MsgTypeDeleteAAPDPContextResponse MsgTypeErrorIndication MsgTypePDUNotificationRequest MsgTypePDUNotificationResponse MsgTypePDUNotificationRejectRequest MsgTypePDUNotificationRejectResponse _ MsgTypeSendRouteingInformationforGPRSRequest // 32 MsgTypeSendRouteingInformationforGPRSResponse MsgTypeFailureReportRequest MsgTypeFailureReportResponse MsgTypeNoteMSGPRSPresentRequest MsgTypeNoteMSGPRSPresentResponse _ _ _ _ _ _ _ _ _ _ MsgTypeIdentificationRequest // 48 MsgTypeIdentificationResponse MsgTypeSGSNContextRequest MsgTypeSGSNContextResponse MsgTypeSGSNContextAcknowledge MsgTypeDataRecordTransferRequest = 240 MsgTypeDataRecordTransferResponse = 241 MsgTypeTPDU = 255 ) // Message is an interface that defines Message message. type Message interface { MarshalTo([]byte) error UnmarshalBinary(b []byte) error MarshalLen() int String() string Version() int MessageType() uint8 MessageTypeName() string TID() string // deprecated SerializeTo([]byte) error DecodeFromBytes(b []byte) error } // Marshal returns the byte sequence generated from a Message instance. // Better to use MarshalXxx instead if you know the name of message to be Serialized. func Marshal(g Message) ([]byte, error) { b := make([]byte, g.MarshalLen()) if err := g.MarshalTo(b); err != nil { return nil, err } return b, nil } // Parse parses the given bytes as a Message. func Parse(b []byte) (Message, error) { if len(b) < 2 { return nil, ErrTooShortToParse } var g Message switch b[1] { case MsgTypeEchoRequest: g = &EchoRequest{} case MsgTypeEchoResponse: g = &EchoResponse{} /* XXX - Implement! case MsgTypeVersionNotSupported: g = &VerNotSupported{} case MsgTypeNodeAliveRequest: g = &NodeAliveReq{} case MsgTypeNodeAliveResponse: g = &NodeAliveRes{} case MsgTypeRedirectionRequest: g = &RedirectionReq{} case MsgTypeRedirectionResponse: g = &RedirectionRes{} */ case MsgTypeCreatePDPContextRequest: g = &CreatePDPContextRequest{} case MsgTypeCreatePDPContextResponse: g = &CreatePDPContextResponse{} case MsgTypeUpdatePDPContextRequest: g = &UpdatePDPContextRequest{} case MsgTypeUpdatePDPContextResponse: g = &UpdatePDPContextResponse{} case MsgTypeDeletePDPContextRequest: g = &DeletePDPContextRequest{} case MsgTypeDeletePDPContextResponse: g = &DeletePDPContextResponse{} /* XXX - Implement! case MsgTypeCreateAAPDPContextRequest: g = &CreateAAPDPContextReq{} case MsgTypeCreateAAPDPContextResponse: g = &CreateAAPDPContextRes{} case MsgTypeDeleteAAPDPContextRequest: g = &DeleteAAPDPContextReq{} case MsgTypeDeleteAAPDPContextResponse: g = &DeleteAAPDPContextRes{} case MsgTypeErrorIndication: g = &ErrorInd{} case MsgTypePDUNotificationRequest: g = &PDUNotificationReq{} case MsgTypePDUNotificationResponse: g = &PDUNotificationRes{} case MsgTypePDUNotificationRejectRequest: g = &PDUNotificationRejectReq{} case MsgTypePDUNotificationRejectResponse: g = &PDUNotificationRejectRes{} case MsgTypeSendRouteingInformationforGPRSRequest: g = &SendRouteingInformationforGPRSReq{} case MsgTypeSendRouteingInformationforGPRSResponse: g = &SendRouteingInformationforGPRSRes{} case MsgTypeFailureReportRequest: g = &FailureReportReq{} case MsgTypeFailureReportResponse: g = &FailureReportRes{} case MsgTypeNoteMSGPRSPresentRequest: g = &NoteMSGPRSPresentReq{} case MsgTypeNoteMSGPRSPresentResponse: g = &NoteMSGPRSPresentRes{} case MsgTypeIdentificationRequest: g = &IdentificationReq{} case MsgTypeIdentificationResponse: g = &IdentificationRes{} case MsgTypeSGSNContextRequest: g = &SGSNContextReq{} case MsgTypeSGSNContextResponse: g = &SGSNContextRes{} case MsgTypeSGSNContextAcknowledge: g = &SGSNContextAck{} case MsgTypeDataRecordTransferRequest: g = &DataRecordTransferReq{} case MsgTypeDataRecordTransferResponse: g = &DataRecordTransferRes{} */ case MsgTypeTPDU: g = &TPDU{} default: g = &Generic{} } if err := g.UnmarshalBinary(b); err != nil { return nil, fmt.Errorf("failed to Parse Message: %w", err) } return g, nil } // Decapsulate decapsulates given bytes and returns Payload in []byte. func Decapsulate(b []byte) ([]byte, error) { header, err := ParseHeader(b) if err != nil { return nil, err } if header.Type != MsgTypeTPDU { return nil, nil } return header.Payload, nil } // Prettify returns a Message in prettified representation in string. // // Note that this relies much on reflect package, and thus the frequent use of // this function may have a serious impact on the performance of your software. func Prettify(m Message) string { name := m.MessageTypeName() header := strings.TrimSuffix(fmt.Sprint(m), "}") v := reflect.Indirect(reflect.ValueOf(m)) n := v.NumField() - 1 fields := make([]*field, n) for i := 1; i < n+1; i++ { // Skip *Header fields[i-1] = &field{name: v.Type().Field(i).Name, maybeIE: v.Field(i).Interface()} } return fmt.Sprintf("{%s: %s, IEs: [%v]}", name, header, strings.Join(prettifyFields(fields), ", ")) } type field struct { name string maybeIE interface{} } func prettifyFields(fields []*field) []string { ret := []string{} for _, field := range fields { if field.maybeIE == nil { ret = append(ret, prettifyIE(field.name, nil)) continue } // TODO: do this recursively? v, ok := field.maybeIE.(*ie.IE) if !ok { // only for AdditionalIEs field if ies, ok := field.maybeIE.([]*ie.IE); ok { vals := make([]string, len(ies)) for i, val := range ies { vals[i] = fmt.Sprint(val) } ret = append(ret, fmt.Sprintf("{%s: [%v]}", field.name, strings.Join(vals, ", "))) } continue } ret = append(ret, prettifyIE(field.name, v)) } return ret } func prettifyIE(name string, i *ie.IE) string { return fmt.Sprintf("{%s: %v}", name, i) } ================================================ FILE: gtpv0/message/message_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes Message into bytes. // // Deprecated: use Marshal instead. func Serialize(m Message) ([]byte, error) { log.Println("Serialize is deprecated. use Marshal instead") return Marshal(m) } // Decode decodes bytes as Message. // // Deprecated: use Parse instead. func Decode(b []byte) (Message, error) { log.Println("Decode is deprecated. use Parse instead") return Parse(b) } ================================================ FILE: gtpv0/message/message_fuzz_test.go ================================================ package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0/message" ) func FuzzParse(f *testing.F) { f.Fuzz(func(t *testing.T, b []byte) { if _, err := message.Parse(b); err != nil { t.Skip() } }) } func FuzzHeaderParse(f *testing.F) { f.Fuzz(func(t *testing.T, b []byte) { if _, err := message.ParseHeader(b); err != nil { t.Skip() } }) } ================================================ FILE: gtpv0/message/t-pdu.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message // TPDU represents a T-PDU type of GTPv0 message. type TPDU struct { *Header } // NewTPDU creates a new TPDU. func NewTPDU(seq, label uint16, tid uint64, payload []byte) *TPDU { t := &TPDU{ Header: NewHeader( 0x1e, MsgTypeTPDU, seq, label, tid, payload, ), } t.SetLength() return t } // Marshal returns the byte sequence generated from a TPDU. func (t *TPDU) Marshal() ([]byte, error) { b := make([]byte, t.MarshalLen()) if err := t.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (t *TPDU) MarshalTo(b []byte) error { if len(b) < t.MarshalLen() { return ErrTooShortToMarshal } t.Header.Payload = t.Payload t.Header.SetLength() return t.Header.MarshalTo(b) } // ParseTPDU parses a given byte sequence as a TPDU. func ParseTPDU(b []byte) (*TPDU, error) { t := &TPDU{} if err := t.UnmarshalBinary(b); err != nil { return nil, err } return t, nil } // UnmarshalBinary parses a given byte sequence as a TPDU. func (t *TPDU) UnmarshalBinary(b []byte) error { var err error t.Header, err = ParseHeader(b) if err != nil { return err } t.Payload = t.Header.Payload return nil } // MarshalLen returns the serial length of Data. func (t *TPDU) MarshalLen() int { return t.Header.MarshalLen() - len(t.Header.Payload) + len(t.Payload) } // SetLength sets the length in Length field. func (t *TPDU) SetLength() { t.Header.Length = uint16(len(t.Payload)) } // MessageTypeName returns the name of protocol. func (t *TPDU) MessageTypeName() string { return "T-PDU" } // TID returns the TID in human-readable string. func (t *TPDU) TID() string { return t.tid() } ================================================ FILE: gtpv0/message/t-pdu_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes TPDU into bytes. // // Deprecated: use TPDU.Marshal instead. func (t *TPDU) Serialize() ([]byte, error) { log.Println("TPDU.Serialize is deprecated. use TPDU.Marshal instead") return t.Marshal() } // SerializeTo serializes TPDU into bytes given as b. // // Deprecated: use TPDU.MarshalTo instead. func (t *TPDU) SerializeTo(b []byte) error { log.Println("TPDU.SerializeTo is deprecated. use TPDU.MarshalTo instead") return t.MarshalTo(b) } // DecodeTPDU decodes bytes as TPDU. // // Deprecated: use ParseTPDU instead. func DecodeTPDU(b []byte) (*TPDU, error) { log.Println("DecodeTPDU is deprecated. use ParseTPDU instead") return ParseTPDU(b) } // DecodeFromBytes decodes bytes as TPDU. // // Deprecated: use TPDU.UnmarshalBinary instead. func (t *TPDU) DecodeFromBytes(b []byte) error { log.Println("TPDU.DecodeFromBytes is deprecated. use TPDU.UnmarshalBinary instead") return t.UnmarshalBinary(b) } // Len returns the actual length of TPDU. // // Deprecated: use TPDU.MarshalLen instead. func (t *TPDU) Len() int { log.Println("TPDU.Len is deprecated. use TPDU.MarshalLen instead") return t.MarshalLen() } ================================================ FILE: gtpv0/message/t-pdu_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestTPDU(t *testing.T) { cases := []testutils.TestCase{ { Description: "normal", Structured: message.NewTPDU( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, []byte{0xde, 0xad, 0xbe, 0xef}, ), Serialized: []byte{ // Header 0x1e, 0xff, 0x00, 0x04, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // SNDPD 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // Payload 0xde, 0xad, 0xbe, 0xef, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseTPDU(b) if err != nil { return nil, err } return v, nil }) } ================================================ FILE: gtpv0/message/update-pdp-context-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv0/ie" ) // UpdatePDPContextRequest is a UpdatePDPContextRequest Header and its IEs above. type UpdatePDPContextRequest struct { *Header RAI *ie.IE QoSProfile *ie.IE Recovery *ie.IE FlowLabelDataI *ie.IE FlowLabelSignalling *ie.IE EndUserAddress *ie.IE SGSNAddressForSignalling *ie.IE SGSNAddressForUserTraffic *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewUpdatePDPContextRequest creates a new UpdatePDPContextRequest. func NewUpdatePDPContextRequest(seq, label uint16, tid uint64, ies ...*ie.IE) *UpdatePDPContextRequest { u := &UpdatePDPContextRequest{ Header: NewHeader( 0x1e, MsgTypeUpdatePDPContextRequest, seq, label, tid, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.RouteingAreaIdentity: u.RAI = i case ie.QualityOfServiceProfile: u.QoSProfile = i case ie.Recovery: u.Recovery = i case ie.FlowLabelDataI: u.FlowLabelDataI = i case ie.FlowLabelSignalling: u.FlowLabelSignalling = i case ie.EndUserAddress: u.EndUserAddress = i case ie.GSNAddress: if u.SGSNAddressForSignalling == nil { u.SGSNAddressForSignalling = i } else { u.SGSNAddressForUserTraffic = i } case ie.PrivateExtension: u.PrivateExtension = i default: u.AdditionalIEs = append(u.AdditionalIEs, i) } } u.SetLength() return u } // Marshal returns the byte sequence generated from a UpdatePDPContextRequest. func (u *UpdatePDPContextRequest) Marshal() ([]byte, error) { b := make([]byte, u.MarshalLen()) if err := u.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (u *UpdatePDPContextRequest) MarshalTo(b []byte) error { if u.Header.Payload != nil { u.Header.Payload = nil } u.Header.Payload = make([]byte, u.MarshalLen()-u.Header.MarshalLen()) offset := 0 if ie := u.RAI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.QoSProfile; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.Recovery; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.FlowLabelDataI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.FlowLabelSignalling; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.EndUserAddress; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.SGSNAddressForSignalling; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.SGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.PrivateExtension; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range u.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(u.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } u.Header.SetLength() return u.Header.MarshalTo(b) } // ParseUpdatePDPContextRequest parses a given byte sequence as a UpdatePDPContextRequest. func ParseUpdatePDPContextRequest(b []byte) (*UpdatePDPContextRequest, error) { u := &UpdatePDPContextRequest{} if err := u.UnmarshalBinary(b); err != nil { return nil, err } return u, nil } // UnmarshalBinary parses a given byte sequence as a UpdatePDPContextRequest. func (u *UpdatePDPContextRequest) UnmarshalBinary(b []byte) error { var err error u.Header, err = ParseHeader(b) if err != nil { return err } if len(u.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(u.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.RouteingAreaIdentity: u.RAI = i case ie.QualityOfServiceProfile: u.QoSProfile = i case ie.Recovery: u.Recovery = i case ie.FlowLabelDataI: u.FlowLabelDataI = i case ie.FlowLabelSignalling: u.FlowLabelSignalling = i case ie.EndUserAddress: u.EndUserAddress = i case ie.GSNAddress: if u.SGSNAddressForSignalling == nil { u.SGSNAddressForSignalling = i } else { u.SGSNAddressForUserTraffic = i } case ie.PrivateExtension: u.PrivateExtension = i default: u.AdditionalIEs = append(u.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (u *UpdatePDPContextRequest) MarshalLen() int { l := u.Header.MarshalLen() - len(u.Header.Payload) if ie := u.RAI; ie != nil { l += ie.MarshalLen() } if ie := u.QoSProfile; ie != nil { l += ie.MarshalLen() } if ie := u.Recovery; ie != nil { l += ie.MarshalLen() } if ie := u.FlowLabelDataI; ie != nil { l += ie.MarshalLen() } if ie := u.FlowLabelSignalling; ie != nil { l += ie.MarshalLen() } if ie := u.EndUserAddress; ie != nil { l += ie.MarshalLen() } if ie := u.SGSNAddressForSignalling; ie != nil { l += ie.MarshalLen() } if ie := u.SGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := u.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range u.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (u *UpdatePDPContextRequest) SetLength() { u.Header.Length = uint16(u.MarshalLen() - 20) } // MessageTypeName returns the name of protocol. func (u *UpdatePDPContextRequest) MessageTypeName() string { return "Update PDP Context Request" } // TID returns the TID in human-readable string. func (u *UpdatePDPContextRequest) TID() string { return u.tid() } ================================================ FILE: gtpv0/message/update-pdp-context-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes UpdatePDPContextRequest into bytes. // // Deprecated: use UpdatePDPContextRequest.Marshal instead. func (u *UpdatePDPContextRequest) Serialize() ([]byte, error) { log.Println("UpdatePDPContextRequest.Serialize is deprecated. use UpdatePDPContextRequest.Marshal instead") return u.Marshal() } // SerializeTo serializes UpdatePDPContextRequest into bytes given as b. // // Deprecated: use UpdatePDPContextRequest.MarshalTo instead. func (u *UpdatePDPContextRequest) SerializeTo(b []byte) error { log.Println("UpdatePDPContextRequest.SerializeTo is deprecated. use UpdatePDPContextRequest.MarshalTo instead") return u.MarshalTo(b) } // DecodeUpdatePDPContextRequest decodes bytes as UpdatePDPContextRequest. // // Deprecated: use ParseUpdatePDPContextRequest instead. func DecodeUpdatePDPContextRequest(b []byte) (*UpdatePDPContextRequest, error) { log.Println("DecodeUpdatePDPContextRequest is deprecated. use ParseUpdatePDPContextRequest instead") return ParseUpdatePDPContextRequest(b) } // DecodeFromBytes decodes bytes as UpdatePDPContextRequest. // // Deprecated: use UpdatePDPContextRequest.UnmarshalBinary instead. func (u *UpdatePDPContextRequest) DecodeFromBytes(b []byte) error { log.Println("UpdatePDPContextRequest.DecodeFromBytes is deprecated. use UpdatePDPContextRequest.UnmarshalBinary instead") return u.UnmarshalBinary(b) } // Len returns the actual length of UpdatePDPContextRequest. // // Deprecated: use UpdatePDPContextRequest.MarshalLen instead. func (u *UpdatePDPContextRequest) Len() int { log.Println("UpdatePDPContextRequest.Len is deprecated. use UpdatePDPContextRequest.MarshalLen instead") return u.MarshalLen() } ================================================ FILE: gtpv0/message/update-pdp-context-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0/ie" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestUpdatePDPContextRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "mandatory-only", Structured: message.NewUpdatePDPContextRequest( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ie.NewQualityOfServiceProfile(1, 1, 1, 1, 1), ie.NewFlowLabelDataI(11), ie.NewFlowLabelSignalling(22), ie.NewEndUserAddress("1.1.1.1"), ie.NewGSNAddress("2.2.2.2"), ie.NewGSNAddress("3.3.3.3"), ), Serialized: []byte{ // Header 0x1e, 0x12, 0x00, 0x21, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // QualityOfServiceProfile 0x06, 0x09, 0x11, 0x01, // FlowLabelDataI 0x10, 0x00, 0x0b, // FlowLabelSignalling 0x11, 0x00, 0x16, // EndUserAddress 0x80, 0x00, 0x06, 0xf1, 0x21, 0x01, 0x01, 0x01, 0x01, // SGSNAddressForSignalling 0x85, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, // SGSNAddressForUserData 0x85, 0x00, 0x04, 0x03, 0x03, 0x03, 0x03, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseUpdatePDPContextRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv0/message/update-pdp-context-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv0/ie" ) // UpdatePDPContextResponse is a UpdatePDPContextResponse Header and its IEs above. type UpdatePDPContextResponse struct { *Header Cause *ie.IE QoSProfile *ie.IE Recovery *ie.IE FlowLabelDataI *ie.IE FlowLabelSignalling *ie.IE ChargingID *ie.IE EndUserAddress *ie.IE GGSNAddressForSignalling *ie.IE GGSNAddressForUserTraffic *ie.IE ChargingGatewayAddress *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewUpdatePDPContextResponse creates a new UpdatePDPContextResponse. func NewUpdatePDPContextResponse(seq, label uint16, tid uint64, ies ...*ie.IE) *UpdatePDPContextResponse { u := &UpdatePDPContextResponse{ Header: NewHeader( 0x1e, MsgTypeUpdatePDPContextResponse, seq, label, tid, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: u.Cause = i case ie.QualityOfServiceProfile: u.QoSProfile = i case ie.Recovery: u.Recovery = i case ie.FlowLabelDataI: u.FlowLabelDataI = i case ie.FlowLabelSignalling: u.FlowLabelSignalling = i case ie.ChargingID: u.ChargingID = i case ie.EndUserAddress: u.EndUserAddress = i case ie.GSNAddress: if u.GGSNAddressForSignalling == nil { u.GGSNAddressForSignalling = i } else { u.GGSNAddressForUserTraffic = i } case ie.ChargingGatewayAddress: u.ChargingGatewayAddress = i case ie.PrivateExtension: u.PrivateExtension = i default: u.AdditionalIEs = append(u.AdditionalIEs, i) } } u.SetLength() return u } // Marshal returns the byte sequence generated from a UpdatePDPContextResponse. func (u *UpdatePDPContextResponse) Marshal() ([]byte, error) { b := make([]byte, u.MarshalLen()) if err := u.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (u *UpdatePDPContextResponse) MarshalTo(b []byte) error { if u.Header.Payload != nil { u.Header.Payload = nil } u.Header.Payload = make([]byte, u.MarshalLen()-u.Header.MarshalLen()) offset := 0 if ie := u.Cause; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.QoSProfile; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.Recovery; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.FlowLabelDataI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.FlowLabelSignalling; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.ChargingID; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.EndUserAddress; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.GGSNAddressForSignalling; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.GGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.ChargingGatewayAddress; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.PrivateExtension; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range u.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(u.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } u.Header.SetLength() return u.Header.MarshalTo(b) } // ParseUpdatePDPContextResponse parses a given byte sequence as a UpdatePDPContextResponse. func ParseUpdatePDPContextResponse(b []byte) (*UpdatePDPContextResponse, error) { u := &UpdatePDPContextResponse{} if err := u.UnmarshalBinary(b); err != nil { return nil, err } return u, nil } // UnmarshalBinary parses a given byte sequence as a UpdatePDPContextResponse. func (u *UpdatePDPContextResponse) UnmarshalBinary(b []byte) error { var err error u.Header, err = ParseHeader(b) if err != nil { return err } if len(u.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(u.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: u.Cause = i case ie.QualityOfServiceProfile: u.QoSProfile = i case ie.Recovery: u.Recovery = i case ie.FlowLabelDataI: u.FlowLabelDataI = i case ie.FlowLabelSignalling: u.FlowLabelSignalling = i case ie.ChargingID: u.ChargingID = i case ie.EndUserAddress: u.EndUserAddress = i case ie.GSNAddress: if u.GGSNAddressForSignalling == nil { u.GGSNAddressForSignalling = i } else { u.GGSNAddressForUserTraffic = i } case ie.ChargingGatewayAddress: u.ChargingGatewayAddress = i case ie.PrivateExtension: u.PrivateExtension = i default: u.AdditionalIEs = append(u.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (u *UpdatePDPContextResponse) MarshalLen() int { l := u.Header.MarshalLen() - len(u.Header.Payload) if ie := u.Cause; ie != nil { l += ie.MarshalLen() } if ie := u.QoSProfile; ie != nil { l += ie.MarshalLen() } if ie := u.Recovery; ie != nil { l += ie.MarshalLen() } if ie := u.FlowLabelDataI; ie != nil { l += ie.MarshalLen() } if ie := u.FlowLabelSignalling; ie != nil { l += ie.MarshalLen() } if ie := u.ChargingID; ie != nil { l += ie.MarshalLen() } if ie := u.EndUserAddress; ie != nil { l += ie.MarshalLen() } if ie := u.GGSNAddressForSignalling; ie != nil { l += ie.MarshalLen() } if ie := u.GGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := u.ChargingGatewayAddress; ie != nil { l += ie.MarshalLen() } if ie := u.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range u.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (u *UpdatePDPContextResponse) SetLength() { u.Header.Length = uint16(u.MarshalLen() - 20) } // MessageTypeName returns the name of protocol. func (u *UpdatePDPContextResponse) MessageTypeName() string { return "Update PDP Context Response" } // TID returns the TID in human-readable string. func (u *UpdatePDPContextResponse) TID() string { return u.tid() } ================================================ FILE: gtpv0/message/update-pdp-context-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes UpdatePDPContextResponse into bytes. // // Deprecated: use UpdatePDPContextResponse.Marshal instead. func (u *UpdatePDPContextResponse) Serialize() ([]byte, error) { log.Println("UpdatePDPContextResponse.Serialize is deprecated. use UpdatePDPContextResponse.Marshal instead") return u.Marshal() } // SerializeTo serializes UpdatePDPContextResponse into bytes given as b. // // Deprecated: use UpdatePDPContextResponse.MarshalTo instead. func (u *UpdatePDPContextResponse) SerializeTo(b []byte) error { log.Println("UpdatePDPContextResponse.SerializeTo is deprecated. use UpdatePDPContextResponse.MarshalTo instead") return u.MarshalTo(b) } // DecodeUpdatePDPContextResponse decodes bytes as UpdatePDPContextResponse. // // Deprecated: use ParseUpdatePDPContextResponse instead. func DecodeUpdatePDPContextResponse(b []byte) (*UpdatePDPContextResponse, error) { log.Println("DecodeUpdatePDPContextResponse is deprecated. use ParseUpdatePDPContextResponse instead") return ParseUpdatePDPContextResponse(b) } // DecodeFromBytes decodes bytes as UpdatePDPContextResponse. // // Deprecated: use UpdatePDPContextResponse.UnmarshalBinary instead. func (u *UpdatePDPContextResponse) DecodeFromBytes(b []byte) error { log.Println("UpdatePDPContextResponse.DecodeFromBytes is deprecated. use UpdatePDPContextResponse.UnmarshalBinary instead") return u.UnmarshalBinary(b) } // Len returns the actual length of UpdatePDPContextResponse. // // Deprecated: use UpdatePDPContextResponse.MarshalLen instead. func (u *UpdatePDPContextResponse) Len() int { log.Println("UpdatePDPContextResponse.Len is deprecated. use UpdatePDPContextResponse.MarshalLen instead") return u.MarshalLen() } ================================================ FILE: gtpv0/message/update-pdp-context-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv0" "github.com/wmnsk/go-gtp/gtpv0/ie" "github.com/wmnsk/go-gtp/gtpv0/message" "github.com/wmnsk/go-gtp/gtpv0/testutils" ) func TestUpdatePDPContextResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "request-accepted", Structured: message.NewUpdatePDPContextResponse( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ie.NewCause(gtpv0.CauseRequestAccepted), ie.NewQualityOfServiceProfile(1, 1, 1, 1, 1), ie.NewFlowLabelDataI(11), ie.NewFlowLabelSignalling(22), ie.NewChargingID(0xff), ie.NewEndUserAddress("1.1.1.1"), ie.NewGSNAddress("2.2.2.2"), ie.NewGSNAddress("3.3.3.3"), ), Serialized: []byte{ // Header 0x1e, 0x13, 0x00, 0x28, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // Cause 0x01, 0x80, // QualityOfServiceProfile 0x06, 0x09, 0x11, 0x01, // FlowLabelDataI 0x10, 0x00, 0x0b, // FlowLabelSignalling 0x11, 0x00, 0x16, // ChargingID 0x7f, 0x00, 0x00, 0x00, 0xff, // EndUserAddress 0x80, 0x00, 0x06, 0xf1, 0x21, 0x01, 0x01, 0x01, 0x01, // GGSNAddressForSignalling 0x85, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, // GGSNAddressForUserData 0x85, 0x00, 0x04, 0x03, 0x03, 0x03, 0x03, }, }, { Description: "no-resources", Structured: message.NewUpdatePDPContextResponse( testutils.TestFlow.Seq, testutils.TestFlow.Label, testutils.TestFlow.TID, ie.NewCause(gtpv0.CauseNoResourcesAvailable), ), Serialized: []byte{ // Header 0x1e, 0x13, 0x00, 0x02, // SequenceNumber 0x00, 0x01, 0x00, 0x00, // Sndpd 0xff, 0xff, 0xff, 0xff, // TID 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x55, // Cause 0x01, 0xc7, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseUpdatePDPContextResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv0/testutils/testutils.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Package testutils is an internal package to be used for unit tests. Don't use this. package testutils import ( "testing" "github.com/pascaldekloe/goe/verify" "github.com/wmnsk/go-gtp/gtpv0/message" ) // Serializable is just for testing gtpv2.Messages. Don't use this. type Serializable interface { Marshal() ([]byte, error) MarshalLen() int } // TestCase is just for testing gtpv2.Messages. Don't use this. type TestCase struct { Description string Structured Serializable Serialized []byte } // ParseFunc is just for testing gtpv2.Messages. Don't use this. type ParseFunc func([]byte) (Serializable, error) // TestFlow is just for testing gtpv2.Messages. Don't use this. var TestFlow = struct { Seq, Label uint16 TID uint64 }{ 0x0001, 0x0000, 0x2143658709214355, } // Run is just for testing gtpv2.Messages. Don't use this. func Run(t *testing.T, cases []TestCase, parse ParseFunc) { t.Helper() for _, c := range cases { t.Run(c.Description, func(t *testing.T) { t.Run("Parse", func(t *testing.T) { v, err := parse(c.Serialized) if err != nil { t.Fatal(err) } if got, want := v, c.Structured; !verify.Values(t, "", got, want) { t.Fail() } }) t.Run("Marshal", func(t *testing.T) { b, err := c.Structured.Marshal() if err != nil { t.Fatal(err) } if got, want := b, c.Serialized; !verify.Values(t, "", got, want) { t.Fail() } }) t.Run("Len", func(t *testing.T) { if got, want := c.Structured.MarshalLen(), len(c.Serialized); got != want { t.Fatalf("got %v want %v", got, want) } }) t.Run("Interface", func(t *testing.T) { // Ignore *Header and Generic in this tests. if _, ok := c.Structured.(*message.Header); ok { return } if _, ok := c.Structured.(*message.Generic); ok { return } Parsed, err := message.Parse(c.Serialized) if err != nil { t.Fatal(err) } if got, want := Parsed.Version(), c.Structured.(message.Message).Version(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := Parsed.MessageType(), c.Structured.(message.Message).MessageType(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := Parsed.MessageTypeName(), c.Structured.(message.Message).MessageTypeName(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := Parsed.TID(), c.Structured.(message.Message).TID(); got != want { t.Fatalf("got %v want %v", got, want) } }) }) } } ================================================ FILE: gtpv1/README.md ================================================ # v1: GTPv1 in Golang Package v1 provides simple and painless handling of GTPv1-C and GTPv1-U protocols in pure Golang. ## Getting Started This package is still under construction. The networking feature is only available for GTPv1-U. GTPv1-C feature would be available in the future. See message and ie directory for what you can do with the current implementation. ### Creating a PDP Context as a client _NOT IMPLEMENTED YET!_ ### Waiting for a PDP Context to be created as a server _NOT IMPLEMENTED YET!_ ### Opening a U-Plane connection Retrieve `UPlaneConn` first, using `DialUPlane` (for client) or `NewUPlaneConn` (for server). #### Client `DialUPlane` sends Echo Request and returns `UPlaneConn` if it succeeds. If you don't need Echo, see Server section. As GTP is UDP-based connection, and there are no session management on `UPlaneConn`, the behavior of `Dial` and `ListenAndServe` is not quite different. ```go uConn, err := v1.Dial(ctx, laddr, raddr) if err != nil { // ... } defer uConn.Close() ``` #### Server Retrieve `UPlaneConn` with `NewUPlaneConn`, and `ListenAndServe` to start listening. ```go uConn := v1.NewUPlaneConn(laddr) if err != nil { // ... } defer uConn.Close() // This blocks, and returns an error when it's fatal. if err := uConn.ListenAndServe(ctx); err != nil { // ... } ``` ### Manupulating `UPlaneConn` With `UPlaneConn`, you can add and delete tunnels, and manipulate device directly. #### Using Linux Kernel GTP-U Linux Kernel GTP-U is quite performant and easy to handle, but it requires root privilege, and of course it works only on Linux. So it is disabled by default. To get started, enable it first. Note that it cannot be disabled while the program is working. ```go if err := uConn.EnableKernelGTP("gtp0", v1.roleSGSN); err != nil { // ... } ``` Then, when the bearer information is ready, use `AddTunnel` or `AddTunnelOverride` to add a tunnel. The latter one deletes the existing tunnel with the same IP and/or incoming TEID before creating a tunnel, while the former fails if there's any duplication. ```go // add a tunnel by giving GTP peer's IP, subscriber's IP, if err := uConn.AddTunnelOverride( net.ParseIP("10.10.10.10"), // GTP peer's IP net.ParseIP("1.1.1.1"), // subscriber's IP 0x55667788, // outgoing TEID 0x11223344, // incoming TEID ); err != nil { // ... } ``` When the tunnel is no longer necessary, use `DelTunnelByITEI` or `DelTunnelByMSAddress` to delete it. Or, by `Close`-ing the `UPlaneConn`, all the tunnels associated will the cleared. ```go // delete a tunnel by giving an incoming TEID. if err := uConn.DelTunnelByITEI(0x11223344); err != nil { // ... } // delete a tunnel by giving an IP address assigned to a subscriber. if err := uConn.DelTunnelByMSAddress(net.ParseIP("1.1.1.1")); err != nil { // ... } ``` The packets NOT forwarded by the Kernel can be handled automatically by giving a handler to `UPlaneConn`. Handlers for T-PDU, Echo Request/Response, and Error Indication are registered by default, but you can override them using `AddHandler`. ```go uConn.AddHandler(message.MsgTypeEchoRequest, func(c v1.Conn, senderAddr net.Addr, msg message.Message) error { // do anything you want for Echo Request here. // errors returned here are passed to `errCh` that is given to UPlaneConn at the beginning. return nil }) ``` If the tunnel with appropriate IP or TEID is not found for a T-PDU packet, Kernel sends it to userland. You can manipulate it with `ReadFromGTP`. ```go buf := make([]byte, 1500) // the 3rd returned value is TEID in GTPv1-U Header. n, raddr, teid, err := uConn.ReadFromGTP(buf) if err != nil { // ... } fmt.Printf("%x", buf[:n]) // prints only the payload, no GTP header included. ``` Also, you can send any payload by using `WriteToGTP`. It writes the given payload with GTP header to the specified addr over `UPlaneConn`. ```go // first return value is the number of bytes written. if _, err := uConn.WriteToGTP(teid, payload, addr); err != nil { // ... } ``` #### Using userland GTP-U **Note:** _package v1 does provide the encapsulation/decapsulation and some networking features, but it does NOT provide routing of the decapsulated packets, nor capturing IP layer and above on the specified interface. This is because such kind of operations cannot be done without platform-specific codes._ You can use to `ReadFromGTP` read the packets coming into uConn. This does not work for the packets which are handled by `RelayTo`. ```go buf := make([]byte, 1500) n, raddr, teid, err := uConn.ReadFromGTP(buf) if err != nil { // ... } fmt.Printf("%x", buf[:n]) // prints the payload encapsulated in the GTP header. ``` Also, you can send any payload by using `WriteToGTP`. It writes the given payload with GTP header to the specified addr over `UPlaneConn`. ```go // first return value is the number of bytes written. if _, err := uConn.WriteToGTP(teid, payload, addr); err != nil { // ... } ``` Especially or SGSN/S-GW-ish nodes(=have multiple GTP tunnels and its raison d'être is just to forward traffic right to left/left to right) we provide a method to swap TEID and forward T-PDU packets automatically and efficiently. By using `RelayTo`, the `UPlaneConn` automatically handles the T-PDU packet in background with the least cost. Note that it's performed on the userland and thus it's not so performant. ```go // this is the example for S-GW that completed establishing a session and ready to forward U-Plane packets. s1uConn.RelayTo(s5uConn, s1usgwTEID, s5uBearer.OutgoingTEID, s5uBearer.RemoteAddress) s5uConn.RelayTo(s1uConn, s5usgwTEID, s1uBearer.OutgoingTEID, s1uBearer.RemoteAddress) ``` ### Handling Extension Headers `AddExtensionHeaders` adds ExtensionHeader(s) to the Header of a Message, set the E flag, and checks if the types given are consistent (error will be returned if not). ```go msg := message.NewTPDU(0x11223344, []byte{0xde, 0xad, 0xbe, 0xef}) if err := msg.AddExtensionHeaders( // We don't support construction of the specific type of an ExtensionHeader. // The second parameter should be the serialized bytes of contents. message.NewExtensionHeader( message.ExtHeaderTypeUDPPort, []byte{0x22, 0xb8}, message.ExtHeaderTypePDUSessionContainer, ), message.NewExtensionHeader( message.ExtHeaderTypePDUSessionContainer, []byte{0x00, 0xc2}, message.ExtHeaderTypeNoMoreExtensionHeaders, ), ); err != nil { // ... } ``` ExtensionHeaders decoded or added are stored in `ExtensionHeaders` field in the Header, which can be accessed like this. ```go // no need to write msg.Header.ExtensionHeaders, as the Header is embedded in messages. for _, eh := range msg.ExtensionHeaders { log.Println(eh.Type) // ExtensionHeader type has its own Type while it's not actually included in a packet. log.Println(eh.Content) // We do not support decoding of each type of content yet. Decode them on your own. log.Println(eh.NextType) // Don't sort the slice - it ruins the packet, or even cause a panic. } ``` When you are directly manipulating a Header for some reason, `WithExtensionHeaders` would help you simplify your operation. Be sure not to call it on a Message, as it returns `*Header`, not a `Message` interface. ```go header := message.NewHeader( 0x30, // no need to set E flag here - With... method will do that instead. message.MsgTypeEchoRequest, 0xdeadbeef, 0x00, []byte{0xde, 0xad, 0xbe, 0xef}, ).WithExtensionHeaders( message.NewExtensionHeader( message.ExtHeaderTypeUDPPort, []byte{0x22, 0xb8}, message.ExtHeaderTypePDUSessionContainer, ), message.NewExtensionHeader( message.ExtHeaderTypePDUSessionContainer, []byte{0x00, 0xc2}, message.ExtHeaderTypeNoMoreExtensionHeaders, ), ) ``` ## Supported Features ### Messages The following Messages marked with "Yes" are currently available with their own useful constructors. _Even there are some missing Messages, you can create any kind of Message by using `message.NewGeneric`._ | ID | Name | Supported | |-----------|---------------------------------------------|-----------| | 0 | (Spare/Reserved) | - | | 1 | Echo Request | Yes | | 2 | Echo Response | Yes | | 3 | Version Not Supported | Yes | | 4 | Node Alive Request | | | 5 | Node Alive Response | | | 6 | Redirection Request | | | 7 | Redirection Response | | | 8-15 | (Spare/Reserved) | - | | 16 | Create PDP Context Request | Yes | | 17 | Create PDP Context Response | Yes | | 18 | Update PDP Context Request | Yes | | 19 | Update PDP Context Response | Yes | | 20 | Delete PDP Context Request | Yes | | 21 | Delete PDP Context Response | Yes | | 22 | Initiate PDP Context Activation Request | | | 23 | Initiate PDP Context Activation Response | | | 24-25 | (Spare/Reserved) | - | | 26 | Error Indication | Yes | | 27 | PDU Notification Request | | | 28 | PDU Notification Response | | | 29 | PDU Notification Reject Request | | | 30 | PDU Notification Reject Response | | | 31 | Supported Extension Headers Notification | Yes | | 32 | Send Routeing Information for GPRS Request | | | 33 | Send Routeing Information for GPRS Response | | | 34 | Failure Report Request | | | 35 | Failure Report Response | | | 36 | Note MS GPRS Present Request | | | 37 | Note MS GPRS Present Response | | | 38-47 | (Spare/Reserved) | - | | 48 | Identification Request | | | 49 | Identification Response | | | 50 | SGSN Context Request | | | 51 | SGSN Context Response | | | 52 | SGSN Context Acknowledge | | | 53 | Forward Relocation Request | | | 54 | Forward Relocation Response | | | 55 | Forward Relocation Complete | | | 56 | Relocation Cancel Request | | | 57 | Relocation Cancel Response | | | 58 | Forward SRNS Context | | | 59 | Forward Relocation Complete Acknowledge | | | 60 | Forward SRNS Context Acknowledge | | | 61 | UE Registration Query Request | | | 62 | UE Registration Query Response | | | 63-69 | (Spare/Reserved) | - | | 70 | RAN Information Relay | | | 71-95 | (Spare/Reserved) | - | | 96 | MBMS Notification Request | | | 97 | MBMS Notification Response | | | 98 | MBMS Notification Reject Request | | | 99 | MBMS Notification Reject Response | | | 100 | Create MBMS Context Request | | | 101 | Create MBMS Context Response | | | 102 | Update MBMS Context Request | | | 103 | Update MBMS Context Response | | | 104 | Delete MBMS Context Request | | | 105 | Delete MBMS Context Response | | | 106 - 111 | (Spare/Reserved) | - | | 112 | MBMS Registration Request | | | 113 | MBMS Registration Response | | | 114 | MBMS De-Registration Request | | | 115 | MBMS De-Registration Response | | | 116 | MBMS Session Start Request | | | 117 | MBMS Session Start Response | | | 118 | MBMS Session Stop Request | | | 119 | MBMS Session Stop Response | | | 120 | MBMS Session Update Request | | | 121 | MBMS Session Update Response | | | 122-127 | (Spare/Reserved) | - | | 128 | MS Info Change Notification Request | | | 129 | MS Info Change Notification Response | | | 130-239 | (Spare/Reserved) | - | | 240 | Data Record Transfer Request | | | 241 | Data Record Transfer Response | | | 242-253 | (Spare/Reserved) | - | | 254 | End Marker | | | 255 | G-PDU | Yes | ### Information Elements The following Information Elements marked with "Yes" are currently supported with their own useful constructors. _Even there are some missing IEs, you can create any kind of IEs by using `ie.New` function or by initializing ie.IE directly._ | ID | Name | Supported | |---------|-------------------------------------------|-----------| | 0 | (Spare/Reserved) | - | | 1 | Cause | Yes | | 2 | IMSI | Yes | | 3 | Routeing Area Identity | Yes | | 4 | Temporary Logical Link Identity | | | 5 | Packet TMSI | Yes | | 6 | (Spare/Reserved) | - | | 7 | (Spare/Reserved) | - | | 8 | Reordering Required | Yes | | 9 | Authentication Triplet | Yes | | 10 | (Spare/Reserved) | - | | 11 | MAP Cause | Yes | | 12 | P-TMSI Signature | Yes | | 13 | MS Validated | Yes | | 14 | Recovery | Yes | | 15 | Selection Mode | Yes | | 16 | TEID Data I | Yes | | 17 | TEID C-Plane | Yes | | 18 | TEID Data II | Yes | | 19 | Teardown Indication | Yes | | 20 | NSAPI | Yes | | 21 | RANAP Cause | Yes | | 22 | RAB Context | | | 23 | Radio Priority SMS | | | 24 | Radio Priority | | | 25 | Packet Flow ID | | | 26 | Charging Characteristics | | | 27 | Trace Reference | | | 28 | Trace Type | | | 29 | MS Not Reachable Reason | | | 30-126 | (Spare/Reserved) | - | | 127 | Charging ID | Yes | | 128 | End User Address | Yes | | 129 | MM Context | | | 130 | PDP Context | | | 131 | Access Point Name | Yes | | 132 | Protocol Configuration Options | Yes | | 133 | GSN Address | Yes | | 134 | MSISDN | Yes | | 135 | QoS Profile | | | 136 | Authentication Quintuplet | Yes | | 137 | Traffic Flow Template | | | 138 | Target Identification | | | 139 | UTRAN Transparent Container | | | 140 | RAB Setup Information | | | 141 | Extension Header Type List | Yes | | 142 | Trigger Id | | | 143 | OMC Identity | | | 144 | RAN Transparent Container | | | 145 | PDP Context Prioritization | | | 146 | Additional RAB Setup Information | | | 147 | SGSN Number | | | 148 | Common Flags | Yes | | 149 | APN Restriction | Yes | | 150 | Radio Priority LCS | | | 151 | RAT Type | Yes | | 152 | User Location Information | Yes | | 153 | MS Time Zone | Yes | | 154 | IMEISV | Yes | | 155 | CAMEL Charging Information Container | | | 156 | MBMS UE Context | | | 157 | Temporary Mobile Group Identity | | | 158 | RIM Routing Address | | | 159 | MBMS Protocol Configuration Options | | | 160 | MBMS Service Area | | | 161 | Source RNC PDCP Context Info | | | 162 | Additional Trace Info | | | 163 | Hop Counter | | | 164 | Selected PLMN Id | | | 165 | MBMS Session Identifier | | | 166 | MBMS 2G/3G Indicator | | | 167 | Enhanced NSAPI | | | 168 | MBMS Session Duration | | | 169 | Additional MBMS Trace Info | | | 170 | MBMS Session Repetition Number | | | 171 | MBMS Time To Data Transfer | | | 172 | (Spare/Reserved) | - | | 173 | BSS Container | | | 174 | Cell Identification | | | 175 | PDU Numbers | | | 176 | BSS GP Cause | | | 177 | Required MBMS Bearer Capabilities | | | 178 | RIM Routing Address Discriminator | | | 179 | List of Setup PFCs | | | 180 | PS Handover XID Parameters | | | 181 | MS Info Change Reporting Action | | | 182 | Direct Tunnel Flags | | | 183 | Correlation Id | | | 184 | Bearer Control Mode | | | 185 | MBMS Flow Identifier | | | 186 | MBMS IP Multicast Distribution | | | 187 | MBMS Distribution Acknowledgement | | | 188 | Reliable InterRAT Handover Info | | | 189 | RFSP Index | | | 190 | Fully Qualified Domain Name | | | 191 | Evolved Allocation Retention Priority I | | | 192 | Evolved Allocation Retention Priority II | | | 193 | Extended Common Flags | Yes | | 194 | User CSG Information | | | 195 | CSG Information Reporting Action | | | 196 | CSG ID | | | 197 | CSG Membership Indication | | | 198 | Aggregate Maximum Bit Rate | | | 199 | UE Network Capability | | | 200 | UE-AMBR | | | 201 | APN-AMBR with NSAPI | | | 202 | GGSN Back-Off Time | | | 203 | Signalling Priority Indication | | | 204 | Signalling Priority Indication with NSAPI | | | 205 | Higher Bitrates than 16Mbps Flag | | | 206 | (Spare/Reserved) | - | | 207 | Additional MM Context for SRVCC | | | 208 | Additional Flags for SRVCC | | | 209 | STN-SR | | | 210 | C-MSISDN | | | 211 | Extended RANAP Cause | | | 212 | eNodeB ID | | | 213 | Selection Mode with NSAPI | | | 214 | ULI Timestamp | Yes | | 215 | LHN Id with NSAPI | | | 216 | CN Operator Selection Entity | | | 217 | UE Usage Type | | | 218 | Extended Common Flags II | Yes | | 219 | Node Identifier | | | 220 | CIoT Optimizations Support Indication | | | 221 | SCEF PDN Connection | | | 222 | IOV Updates Counter | | | 223-237 | (Spare/Reserved) | - | | 238 | Special IE Type for IE Type Extension | | | 239-250 | (Spare/Reserved) | - | | 251 | Charging Gateway Address | | | 252-254 | (Spare/Reserved) | - | | 255 | Private Extension | | ================================================ FILE: gtpv1/conn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 import ( "net" "github.com/wmnsk/go-gtp/gtpv1/message" ) // Conn is an abstraction of both GTPv1-C and GTPv1-U Conn. type Conn interface { net.PacketConn AddHandler(uint8, HandlerFunc) RespondTo(net.Addr, message.Message, message.Message) error Restarts() uint8 } ================================================ FILE: gtpv1/constants.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 // Registered UDP ports const ( GTPCPort = ":2123" GTPUPort = ":2152" ) // Cause definitions. const ( ReqCauseRequestIMSI uint8 = iota ReqCauseRequestIMEI ReqCauseRequestIMSIAndIMEI ReqCauseNoIdentityNeeded ReqCauseMSRefuses ReqCauseMSIsNotGPRSResponding ReqCauseReactivationRequested ReqCausePDPAddressInactivityTimerExpires ReqCauseNetworkFailure ReqCauseQoSParameterMismatch // 10-127: for future use / reserved for prime ) // Cause definitions. const ( ResCauseRequestAccepted uint8 = iota + 128 ResCauseNewPDPTypeDueToNetworkPreference ResCauseNewPDPTypeDueToSingleAddressBearerOnly // 131-191: for future use / reserved for prime. ) // Cause definitions. const ( ResCauseNonExistent uint8 = iota + 192 ResCauseInvalidMessageFormat ResCauseIMSIIMEINotKnown ResCauseMSIsGPRSDetached ResCauseMSIsNotGPRSResponding ResCauseMSRefuses ResCauseVersionNotSupported ResCauseNoResourcesAvailable ResCauseServiceNotSupported ResCauseMandatoryIEIncorrect ResCauseMandatoryIEMissing ResCauseOptionalIEIncorrect ResCauseSystemFailure ResCauseRoamingRestriction ResCausePTMSISignatureMismatch ResCauseGPRSConnectionSuspended ResCauseAuthenticationFailure ResCauseUserAuthenticationFailed ResCauseContextNotFound ResCauseAllDynamicPDPAddressesAreOccupied ResCauseNoMemoryIsAvailable ResCauseRelocationFailure ResCauseUnknownMandatoryExtensionHeader ResCauseSemanticErrorInTheTFTOperation ResCauseSyntacticErrorInTheTFTOperation ResCauseSemanticErrorsInPacketFilter ResCauseSyntacticErrorsInPacketFilter ResCauseMissingOrUnknownAPN ResCauseUnknownPDPAddressOrPDPType ResCausePDPContextWithoutTFTAlreadyActivated ResCauseAPNAccessDeniedNoSubscription ResCauseAPNRestrictionTypeIncompatibilityWithCurrentlyActivePDPContexts ResCauseMSMBMSCapabilitiesInsufficient ResCauseInvalidCorrelationID ResCauseMBMSBearerContextSuperseded ResCauseBearerControlModeViolation ResCauseCollisionWithNetworkInitiatedRequest ResCauseAPNCongestion ResCauseBearerHandlingNotSupported ResCauseTargetAccessRestrictedForTheSubscriber ResCauseUEIsTemporarilyNotReachableDueToPowerSaving ResCauseRelocationFailureDueToNASMessageRedirection // 234-255: for future use / reserved for prime. ) // SelectionMode definitions. const ( SelectionModeMSorNetworkProvidedAPNSubscribedVerified uint8 = iota | 0xf0 SelectionModeMSProvidedAPNSubscriptionNotVerified SelectionModeNetworkProvidedAPNSubscriptionNotVerified ) // PDP Type Organization definitions. const ( PDPTypeETSI uint8 = iota | 0xf0 PDPTypeIETF ) // Protocol ID definitions. // For more identifiers, see RFC 3232. const ( ProtoIDLCP uint16 = 0xc021 ProtoIDPAP uint16 = 0xc023 ProtoIDCHAP uint16 = 0xc223 ProtoIDIPCP uint16 = 0x8021 ) // Container ID definitions. const ( _ uint16 = iota ContIDPCSCFIPv6AddressRequest ContIDIMCNSubsystemSignalingFlag ContIDDNSServerIPv6AddressRequest ContIDNotSupported ContIDMSSupportofNetworkRequestedBearerControlIndicator _ ContIDDSMIPv6HomeAgentAddressRequest ContIDDSMIPv6HomeNetworkPrefixRequest ContIDDSMIPv6IPv4HomeAgentAddressRequest ContIDIPaddressAllocationViaNASSignalling ContIDIPv4addressAllocationViaDHCPv4 ContIDPCSCFIPv4AddressRequest ContIDDNSServerIPv4AddressRequest ContIDMSISDNRequest ContIDIFOMSupportRequest ContIDIPv4LinkMTURequest ContIDMSSupportOfLocalAddressInTFTIndicator ContIDPCSCFReselectionSupport ContIDNBIFOMRequestIndicator ContIDNBIFOMMode ContIDNonIPLinkMTURequest ContIDAPNRateControlSupportIndicator ContID3GPPPSDataOffUEStatus ContIDReliableDataServiceRequestIndicator ContIDAdditionalAPNRateControlForExceptionDataSupportIndicator ContIDPDUSessionID _ _ _ _ _ ContIDEthernetFramePayloadMTURequest ContIDUnstructuredLinkMTURequest ContID5GSMCauseValue ) // Configuration Protocol definitions. const ( ConfigProtocolPPPWithIP uint8 = 0 ) // RATType definitions. const ( _ uint8 = iota RatTypeUTRAN RatTypeGERAN RatTypeWLAN RatTypeGAN RatTypeHSPAEvolution RatTypeEUTRAN ) // UserLocationInformation GeographicLocationType definitions. const ( LocTypeCGI uint8 = iota LocTypeSAI LocTypeRAI ) // APN Restriction definitions. const ( APNRestrictionNoExistingContextsorRestriction uint8 = iota APNRestrictionPublic1 APNRestrictionPublic2 APNRestrictionPrivate1 APNRestrictionPrivate2 ) // MAP Cause definitions. const ( _ uint8 = iota MAPCauseUnknownSubscriber MAPCauseUnknownBaseStation MAPCauseUnknownMSC MAPCauseSecureTransportError MAPCauseUnidentifiedSubscriber MAPCauseAbsentSubscriberSM MAPCauseUnknownEquipment MAPCauseRoamingNotAllowed MAPCauseIllegalSubscriber MAPCauseBearerServiceNotProvisioned MAPCauseTeleserviceNotProvisioned MAPCauseIllegalEquipment MAPCauseCallBarred MAPCauseForwardingViolation MAPCauseCUGReject MAPCauseIllegalSSOperation MAPCauseSSErrorStatus MAPCauseSSNotAvailable MAPCauseSSSubscriptionViolatio MAPCauseSSIncompatibility MAPCauseFacilityNotSupported MAPCauseOngoingGroupCall MAPCauseInvalidTargetBaseStation MAPCauseNoRadioResourceAvailable MAPCauseNoHandoverNumberAvailable MAPCauseSubsequentHandoverFailure MAPCauseAbsentSubscriber MAPCauseIncompatibleTerminal MAPCauseShortTermDenial MAPCauseLongTermDenial MAPCauseSubscriberBusyForMTSMS MAPCauseSMDeliveryFailure MAPCauseMessageWaitingListFull MAPCauseSystemFailure MAPCauseDataMissing MAPCauseUnexpectedDataValue MAPCausePWRegistrationFailure MAPCauseNegativePWCheck MAPCauseNoRoamingNumberAvailable MAPCauseTracingBufferFull _ MAPCauseTargetCellOutsideGroupCallArea MAPCauseNumberOfPWAttemptsViolation MAPCauseNumberChanged MAPCauseBusySubscriber MAPCauseNoSubscriberReply MAPCauseForwardingFailed MAPCauseORNotAllowed MAPCauseATINotAllowed MAPCauseNoGroupCallNumberAvailable MAPCauseResourceLimitation MAPCauseUnauthorizedRequestingNetwork MAPCauseUnauthorizedLCSClient MAPCausePositionMethodFailure _ _ _ MAPCauseUnknownOrUnreachableLCSClient MAPCauseMMEventNotSupported MAPCauseATSINotAllowed MAPCauseATMNotAllowed MAPCauseInformationNotAvailabl _ _ _ _ _ _ _ _ MAPCauseUnknownAlphabe MAPCauseUSSDBusy ) // RANAP Cause definitions. const ( _ uint8 = iota RABPreempted RANAPCauseTrelocoverallExpiry RANAPCauseTrelocprepExpiry RANAPCauseTreloccompleteExpiry RANAPCauseTqueuingExpiry RANAPCauseRelocationTriggered RANAPCauseTRELOCallocExpiry RANAPCauseUnableToEstablishDuringRelocation RANAPCauseUnknownTargetRNC RANAPCauseRelocationCancelled RANAPCauseSuccessfulRelocation RANAPCauseRequestedCipheringIntegrityProtectionAlgorithmsNotSupported RANAPCauseChangeOfCipheringIntegrityProtectionIsNotSupported RANAPCauseFailureInTheRadioInterfaceProcedure RANAPCauseReleaseDueToUTRANGeneratedReason RANAPCauseUserInactivity RANAPCauseTimeCriticalRelocation RANAPCauseRequestedTrafficClassNotAvailable RANAPCauseInvalidRABParametersValue RANAPCauseRequestedMaximumBitRateNotAvailable RANAPCauseRequestedGuaranteedBitRateNotAvailable RANAPCauseRequestedTransferDelayNotAchievable RANAPCauseInvalidRABParametersCombination RANAPCauseConditionViolationForSDUParameters RANAPCauseConditionViolationForTrafficHandlingPriority RANAPCauseConditionViolationForGuaranteedBitRate RANAPCauseUserPlaneVersionsNotSupported RANAPCauseIuUPFailure RANAPCauseRelocationFailureInTargetCNRNCOrTargetSystem RANAPCauseInvalidRABID RANAPCauseNoRemainingRAB RANAPCauseInteractionWithOtherProcedure RANAPCauseRequestedMaximumBitRateForDLNotAvailable RANAPCauseRequestedMaximumBitRateForULNotAvailable RANAPCauseRequestedGuaranteedBitRateForDLNotAvailable RANAPCauseRequestedGuaranteedBitRateForULNotAvailable RANAPCauseRepeatedIntegrityCheckingFailure RANAPCauseRequestedReportTypeNotSupported RANAPCauseRequestSuperseded RANAPCauseReleaseDueToUEWenRatedSignallingConnectionRelease RANAPCauseResourceOptimisationRelocation RANAPCauseRequestedInformationNotAvailable RANAPCauseRelocationDesirableForRadioReasons RANAPCauseRelocationNotSupportedInTargetRNCOrTargetSystem RANAPCauseDirectedRetry RANAPCauseRadioConnectionWithUELost RANAPCauseRNCUnableToEstablishAllRFCs RANAPCauseDecipheringKeysNotAvailable RANAPCauseDedicatedAssistanceDataNotAvailable RANAPCauseRelocationTargetNotAllowed RANAPCauseLocationReportingCongestion RANAPCauseReduceLoadInServingCell RANAPCauseNoRadioResourcesAvailableInTargetCell RANAPCauseGERANIuModeFailure RANAPCauseAccessRestrictedDueToSharedNetworks RANAPCauseIncomingRelocationNotSupportedDueTodPUESBINEFeature RANAPCauseTrafficLoadInTheTargetCellHigherThanInTheSourceCell RANAPCauseMBMSNoMulticastServiceForThisUE RANAPCauseMBMSUnknownUEID RANAPCauseSuccessfulMBMSSessionStartNoDataBearerNecessary RANAPCauseMBMSSupersededDueToNNSF RANAPCauseMBMSUELinkingAlreadyDone RANAPCauseMBMSUEDeLinkingFailureNoExistingUELinking RANAPCauseTMGIUnknown RANAPCauseSignallingTransportResourceFailure RANAPCauseIuTransportConnectionFailedToEstablish _ _ _ _ _ _ _ _ _ _ _ _ _ _ RANAPCauseUserRestrictionStartIndication RANAPCauseUserRestrictionEndIndication RANAPCauseNormalRelease _ _ _ _ _ _ _ _ _ _ _ _ _ RANAPCauseTransferSyntaxError RANAPCauseSemanticError RANAPCauseMessageNotCompatibleWithReceiverState RANAPCauseAbstractSyntaxErrorReject RANAPCauseAbstractSyntaxErrorIgnoreAndNotify RANAPCauseAbstractSyntaxErrorFalselyConstructedMessage _ _ _ _ _ _ _ _ _ _ RANAPCauseOAMIntervention RANAPCauseNoResourceAvailable RANAPCauseUnspecifiedFailure RANAPCauseNetworkOptimisation ) ================================================ FILE: gtpv1/doc.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Package gtpv1 provides simple and painless handling of GTPv1-C and GTPv1-U protocol in pure Golang. // // Please see README.md for detailed usage of the APIs provided by this package. // // https://github.com/wmnsk/go-gtp/blob/main/gtpv1/README.md package gtpv1 ================================================ FILE: gtpv1/errors.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 import ( "errors" "fmt" ) var ( // ErrUnexpectedType indicates that the type of incoming message is not expected. ErrUnexpectedType = errors.New("got unexpected type of message") // ErrInvalidConnection indicates that the connection type(C-Plane or U-Plane) is // not the expected one. ErrInvalidConnection = errors.New("got invalid connection type") // ErrConnNotOpened indicates that some operation is failed due to the status of // Conn is not valid. ErrConnNotOpened = errors.New("connection is not opened") ) // ErrorIndicatedError indicates that Error Indication message is received on U-Plane Connection. type ErrorIndicatedError struct { TEID uint32 Peer string } func (e *ErrorIndicatedError) Error() string { return fmt.Sprintf("error received from %s, TEIDDataI: %#x", e.Peer, e.TEID) } // HandlerNotFoundError indicates that the handler func is not registered in *Conn // for the incoming GTPv2 message. In usual cases this error should not be taken // as fatal, as the other endpoint can make your program stop working just by // sending unregistered message. type HandlerNotFoundError struct { MsgType string } // Error returns violating message type to handle. func (e *HandlerNotFoundError) Error() string { return fmt.Sprintf("no handlers found for incoming message: %s, ignoring", e.MsgType) } ================================================ FILE: gtpv1/gtpv1_fuzz_test.go ================================================ package gtpv1_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1" ) func FuzzDecapsulate(f *testing.F) { f.Fuzz(func(t *testing.T, b []byte) { if _, _, err := gtpv1.Decapsulate(b); err != nil { t.Skip() } }) } ================================================ FILE: gtpv1/handlers.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 import ( "net" "sync" "time" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" ) // HandlerFunc is a handler for specific GTPv1 message. type HandlerFunc func(c Conn, senderAddr net.Addr, msg message.Message) error type msgHandlerMap struct { syncMap sync.Map } func (m *msgHandlerMap) store(msgType uint8, handler HandlerFunc) { m.syncMap.Store(msgType, handler) } func (m *msgHandlerMap) load(msgType uint8) (HandlerFunc, bool) { handler, ok := m.syncMap.Load(msgType) if !ok { return nil, false } return handler.(HandlerFunc), true } func newMsgHandlerMap(m map[uint8]HandlerFunc) *msgHandlerMap { mhm := &msgHandlerMap{syncMap: sync.Map{}} for k, v := range m { mhm.store(k, v) } return mhm } func newDefaultMsgHandlerMap() *msgHandlerMap { return newMsgHandlerMap( map[uint8]HandlerFunc{ message.MsgTypeTPDU: handleTPDU, message.MsgTypeEchoRequest: handleEchoRequest, message.MsgTypeEchoResponse: handleEchoResponse, message.MsgTypeErrorIndication: handleErrorIndication, }, ) } // handleTPDU responds to sender with ErrorIndication by default. // By disabling it(DisableErrorIndication), it passes unhandled T-PDU to // user, which can be caught by calling ReadFromGTP. func handleTPDU(c Conn, senderAddr net.Addr, msg message.Message) error { // this should never happen, as the type should have been assured by // msgHandlerMap before this function is called. pdu, ok := msg.(*message.TPDU) if !ok { return ErrUnexpectedType } u, ok := c.(*UPlaneConn) if !ok { return ErrInvalidConnection } if u.errIndEnabled { if err := u.ErrorIndication(senderAddr, pdu); err != nil { logf("failed to send Error Indication to %s: %v", senderAddr, err) } return nil } tpdu := &tpduSet{ raddr: senderAddr, teid: pdu.TEID(), seq: pdu.Sequence(), payload: pdu.Payload, } // wait for the T-PDU passed to u.tpduCh to be read by ReadFromGTP. // if it got stuck for 3 seconds, it discards the T-PDU received. go func() { select { case u.tpduCh <- tpdu: return case <-time.After(3 * time.Second): return } }() return nil } func handleEchoRequest(c Conn, senderAddr net.Addr, msg message.Message) error { // this should never happen, as the type should have been assured by // msgHandlerMap before this function is called. if _, ok := msg.(*message.EchoRequest); !ok { return ErrUnexpectedType } // respond with EchoResponse. return c.RespondTo( senderAddr, msg, message.NewEchoResponse(0, ie.NewRecovery(c.Restarts())), ) } func handleEchoResponse(c Conn, senderAddr net.Addr, msg message.Message) error { // this should never happen, as the type should have been assured by // msgHandlerMap before this function is called. if _, ok := msg.(*message.EchoResponse); !ok { return ErrUnexpectedType } // do nothing. return nil } func handleErrorIndication(c Conn, senderAddr net.Addr, msg message.Message) error { // this should never happen, as the type should have been assured by // msgHandlerMap before this function is called. ind, ok := msg.(*message.ErrorIndication) if !ok { return ErrUnexpectedType } // just log and return logf("Ignored Error Indication: %v", &ErrorIndicatedError{ TEID: ind.TEIDDataI.MustTEID(), Peer: ind.GTPUPeerAddress.MustIPAddress(), }) return nil } ================================================ FILE: gtpv1/ie/apn-restriction.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewAPNRestriction creates a new APNRestriction IE. func NewAPNRestriction(restriction uint8) *IE { return newUint8ValIE(APNRestriction, restriction) } // APNRestriction returns APNRestriction in uint8 if type matches. func (i *IE) APNRestriction() (uint8, error) { if i.Type != APNRestriction { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustAPNRestriction returns APNRestriction in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustAPNRestriction() uint8 { v, _ := i.APNRestriction() return v } ================================================ FILE: gtpv1/ie/apn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "strings" ) // NewAccessPointName creates a new AccessPointName IE. func NewAccessPointName(apn string) *IE { i := New(AccessPointName, make([]byte, len(apn)+1)) var offset = 0 for _, label := range strings.Split(apn, ".") { l := len(label) i.Payload[offset] = uint8(l) copy(i.Payload[offset+1:], label) offset += l + 1 } return i } // AccessPointName returns AccessPointName in string if type of IE matches. func (i *IE) AccessPointName() (string, error) { if i.Type != AccessPointName { return "", &InvalidTypeError{Type: i.Type} } var ( apn []string offset int ) max := len(i.Payload) for { if offset >= max { break } l := int(i.Payload[offset]) if offset+l+1 > max { return "", io.ErrUnexpectedEOF } apn = append(apn, string(i.Payload[offset+1:offset+l+1])) offset += l + 1 } return strings.Join(apn, "."), nil } // MustAccessPointName returns AccessPointName in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustAccessPointName() string { v, _ := i.AccessPointName() return v } ================================================ FILE: gtpv1/ie/authentication-quintuplet.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewAuthenticationQuintuplet creates a new AuthenticationQuintuplet IE. func NewAuthenticationQuintuplet(rand, xres, ck, ik, autn []byte) *IE { xresLen := len(xres) autnLen := len(autn) i := New(AuthenticationQuintuplet, make([]byte, 16+1+xresLen+16+16+1+autnLen)) copy(i.Payload[0:16], rand) i.Payload[16] = uint8(xresLen) offset := 17 // variable length appears from here. copy(i.Payload[offset:offset+xresLen], xres) offset += xresLen copy(i.Payload[offset:offset+16], ck) offset += 16 copy(i.Payload[offset:offset+16], ik) offset += 16 i.Payload[offset] = uint8(autnLen) offset++ copy(i.Payload[offset:offset+autnLen], autn) return i } // AuthenticationQuintuplet returns AuthenticationQuintuplet in []byte if type matches. func (i *IE) AuthenticationQuintuplet() ([]byte, error) { if i.Type != AuthenticationQuintuplet { return nil, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return nil, io.ErrUnexpectedEOF } return i.Payload, nil } // MustAuthenticationQuintuplet returns AuthenticationQuintuplet in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustAuthenticationQuintuplet() []byte { v, _ := i.AuthenticationQuintuplet() return v } // XRES returns XRES in []byte if type matches. func (i *IE) XRES() ([]byte, error) { if len(i.Payload) == 0 { return nil, nil } switch i.Type { case AuthenticationQuintuplet: if len(i.Payload) < 17 { return nil, io.ErrUnexpectedEOF } xresLen := i.Payload[16] if len(i.Payload) < 17+int(xresLen) { return nil, io.ErrUnexpectedEOF } return i.Payload[17 : 17+int(xresLen)], nil default: return nil, &InvalidTypeError{Type: i.Type} } } // MustXRES returns XRES in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustXRES() []byte { v, _ := i.XRES() return v } // CK returns CK in []byte if type matches. func (i *IE) CK() ([]byte, error) { switch i.Type { case AuthenticationQuintuplet: if len(i.Payload) < 18 { return nil, io.ErrUnexpectedEOF } offset := 17 + int(i.Payload[16]) if len(i.Payload) < offset+16 { return nil, io.ErrUnexpectedEOF } return i.Payload[offset : offset+16], nil default: return nil, &InvalidTypeError{Type: i.Type} } } // MustCK returns CK in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustCK() []byte { v, _ := i.CK() return v } // IK returns IK in []byte if type matches. func (i *IE) IK() ([]byte, error) { switch i.Type { case AuthenticationQuintuplet: if len(i.Payload) < 34 { return nil, io.ErrUnexpectedEOF } offset := 33 + int(i.Payload[16]) if len(i.Payload) < offset+16 { return nil, io.ErrUnexpectedEOF } return i.Payload[offset : offset+16], nil default: return nil, &InvalidTypeError{Type: i.Type} } } // MustIK returns IK in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustIK() []byte { v, _ := i.IK() return v } // AUTN returns AUTN in []byte if type matches. func (i *IE) AUTN() ([]byte, error) { switch i.Type { case AuthenticationQuintuplet: if len(i.Payload) < 50 { return nil, io.ErrUnexpectedEOF } offset := 49 + int(i.Payload[16]) autnLen := i.Payload[50] if len(i.Payload) < offset+int(autnLen) { return nil, io.ErrUnexpectedEOF } return i.Payload[offset : offset+int(autnLen)], nil default: return nil, &InvalidTypeError{Type: i.Type} } } // MustAUTN returns AUTN in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustAUTN() []byte { v, _ := i.AUTN() return v } ================================================ FILE: gtpv1/ie/authentication-triplet.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewAuthenticationTriplet creates a new AuthenticationTriplet IE. func NewAuthenticationTriplet(rand, sres, kc []byte) *IE { i := New(AuthenticationTriplet, make([]byte, 28)) copy(i.Payload[0:16], rand) copy(i.Payload[16:20], sres) copy(i.Payload[20:28], kc) return i } // AuthenticationTriplet returns AuthenticationTriplet in []byte if type matches. func (i *IE) AuthenticationTriplet() ([]byte, error) { if i.Type != AuthenticationTriplet { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustAuthenticationTriplet returns AuthenticationTriplet in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustAuthenticationTriplet() []byte { v, _ := i.AuthenticationTriplet() return v } // RAND returns RAND in []byte if type matches. func (i *IE) RAND() ([]byte, error) { switch i.Type { case AuthenticationTriplet, AuthenticationQuintuplet: if len(i.Payload) < 16 { return nil, io.ErrUnexpectedEOF } return i.Payload[0:16], nil default: return nil, &InvalidTypeError{Type: i.Type} } } // MustRAND returns RAND in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustRAND() []byte { v, _ := i.RAND() return v } // SRES returns SRES in []byte if type matches. func (i *IE) SRES() ([]byte, error) { switch i.Type { case AuthenticationTriplet: if len(i.Payload) < 20 { return nil, io.ErrUnexpectedEOF } return i.Payload[16:20], nil default: return nil, &InvalidTypeError{Type: i.Type} } } // MustSRES returns SRES in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustSRES() []byte { v, _ := i.SRES() return v } // Kc returns Kc in []byte if type matches. func (i *IE) Kc() ([]byte, error) { switch i.Type { case AuthenticationTriplet: if len(i.Payload) < 28 { return nil, io.ErrUnexpectedEOF } return i.Payload[20:28], nil default: return nil, &InvalidTypeError{Type: i.Type} } } // MustKc returns Kc in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustKc() []byte { v, _ := i.Kc() return v } ================================================ FILE: gtpv1/ie/cause.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewCause creates a new Cause IE. func NewCause(cause uint8) *IE { return newUint8ValIE(Cause, cause) } // Cause returns the Cause value if type matches. func (i *IE) Cause() (uint8, error) { if i.Type != Cause { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustCause returns Cause in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustCause() uint8 { v, _ := i.Cause() return v } ================================================ FILE: gtpv1/ie/charging-id.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewChargingID creates a new ChargingID IE. func NewChargingID(id uint32) *IE { return newUint32ValIE(ChargingID, id) } // ChargingID returns the ChargingID value in uint32 if the type of IE matches. func (i *IE) ChargingID() (uint32, error) { if i.Type != ChargingID { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload), nil } // MustChargingID returns ChargingID in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustChargingID() uint32 { v, _ := i.ChargingID() return v } ================================================ FILE: gtpv1/ie/common-flags.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewCommonFlags creates a new CommonFlags IE. // // Note: each flag should be set in 1 or 0. func NewCommonFlags(dualAddr, upgradeQoS, nrsn, noQoS, mbmsCount, ranReady, mbmsService, prohibitComp int) *IE { return New( CommonFlags, []byte{uint8( dualAddr<<7 | upgradeQoS<<6 | nrsn<<5 | noQoS<<4 | mbmsCount<<3 | ranReady<<2 | mbmsService<<1 | prohibitComp, )}, ) } // CommonFlags returns CommonFlags value if type matches. func (i *IE) CommonFlags() (uint8, error) { if i.Type != CommonFlags { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustCommonFlags returns CommonFlags in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustCommonFlags() uint8 { v, _ := i.CommonFlags() return v } // IsDualAddressBearer checks if DualAddressBearer flag exists in CommonFlags. func (i *IE) IsDualAddressBearer() bool { return ((i.MustCommonFlags() >> 7) & 0x01) != 0 } // IsUpgradeQoSSupported checks if UpgradeQoSSupported flag exists in CommonFlags. func (i *IE) IsUpgradeQoSSupported() bool { return ((i.MustCommonFlags() >> 6) & 0x01) != 0 } // IsNRSN checks if NRSN flag exists in CommonFlags. func (i *IE) IsNRSN() bool { return ((i.MustCommonFlags() >> 5) & 0x01) != 0 } // IsNoQoSNegotiation checks if NoQoSNegotiation flag exists in CommonFlags. func (i *IE) IsNoQoSNegotiation() bool { return ((i.MustCommonFlags() >> 4) & 0x01) != 0 } // IsMBMSCountingInformation checks if MBMSCountingInformation flag exists in CommonFlags. func (i *IE) IsMBMSCountingInformation() bool { return ((i.MustCommonFlags() >> 3) & 0x01) != 0 } // IsRANProceduresReady checks if RANProceduresReady flag exists in CommonFlags. func (i *IE) IsRANProceduresReady() bool { return ((i.MustCommonFlags() >> 2) & 0x01) != 0 } // IsMBMSServiceType checks if MBMSServiceType flag exists in CommonFlags. func (i *IE) IsMBMSServiceType() bool { return ((i.MustCommonFlags() >> 1) & 0x01) != 0 } // IsProhibitPayloadCompression checks if ProhibitPayloadCompression flag exists in CommonFlags. func (i *IE) IsProhibitPayloadCompression() bool { return (i.MustCommonFlags() & 0x01) != 0 } ================================================ FILE: gtpv1/ie/end-user-address.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "net" ) const ( pdpTypeETSI uint8 = iota | 0xf0 pdpTypeIETF ) // NewEndUserAddress creates a new EndUserAddress IE from the given IP Address in string. // // The addr can be either IPv4 or IPv6. If the address type is PPP, // just put "ppp" in addr parameter or use NewEndUserAddressPPP func instead. func NewEndUserAddress(addr string) *IE { if addr == "ppp" { return NewEndUserAddressPPP() } return NewEndUserAddressByIP(net.ParseIP(addr)) } // NewEndUserAddressByIP creates a new EndUserAddress IE from net.IP. func NewEndUserAddressByIP(ip net.IP) *IE { if ip == nil { return nil } v4 := ip.To4() // IPv4 if v4 != nil { return newEUAddrV4(v4) } return newEUAddrV6(ip) } // NewEndUserAddressIPv4 creates a new EndUserAddress IE with IPv4. func NewEndUserAddressIPv4(addr string) *IE { v4 := net.ParseIP(addr).To4() if v4 == nil { return New(EndUserAddress, []byte{0xf1, 0x21}) } return newEUAddrV4(v4) } // NewEndUserAddressIPv6 creates a new EndUserAddress IE with IPv6. func NewEndUserAddressIPv6(addr string) *IE { v6 := net.ParseIP(addr).To16() if v6 == nil { return New(EndUserAddress, []byte{0xf1, 0x57}) } return newEUAddrV6(v6) } func newEUAddrV4(v4 []byte) *IE { e := New( EndUserAddress, make([]byte, 6), ) e.Payload[0] = pdpTypeIETF e.Payload[1] = 0x21 copy(e.Payload[2:], v4) return e } func newEUAddrV6(v6 []byte) *IE { e := New( EndUserAddress, make([]byte, 18), ) e.Payload[0] = pdpTypeIETF e.Payload[1] = 0x57 copy(e.Payload[2:], v6) return e } // NewEndUserAddressPPP creates a new EndUserAddress IE with PPP. func NewEndUserAddressPPP() *IE { e := New(EndUserAddress, make([]byte, 2)) e.Payload[0] = pdpTypeETSI e.Payload[1] = pdpTypeIETF e.SetLength() return e } // EndUserAddress returns EndUserAddress value if type matches. func (i *IE) EndUserAddress() ([]byte, error) { if i.Type != EndUserAddress { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustEndUserAddress returns EndUserAddress in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustEndUserAddress() []byte { v, _ := i.EndUserAddress() return v } // PDPTypeOrganization returns PDPTypeOrganization if type matches. func (i *IE) PDPTypeOrganization() (uint8, error) { if i.Type != EndUserAddress { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustPDPTypeOrganization returns PDPTypeOrganization in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPDPTypeOrganization() uint8 { v, _ := i.PDPTypeOrganization() return v } // PDPTypeNumber returns PDPTypeNumber if type matches. func (i *IE) PDPTypeNumber() (uint8, error) { if i.Type != EndUserAddress { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return i.Payload[1], nil } // MustPDPTypeNumber returns PDPTypeNumber in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPDPTypeNumber() uint8 { v, _ := i.PDPTypeNumber() return v } ================================================ FILE: gtpv1/ie/errors.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "errors" "fmt" ) // Error definitions. var ( ErrInvalidLength = errors.New("got invalid length ") ErrTooShortToMarshal = errors.New("too short to serialize") ErrTooShortToParse = errors.New("too short to decode as GTPv1 IE") ErrMalformed = errors.New("malformed IE") ) // InvalidTypeError indicates the type of IE is invalid. type InvalidTypeError struct { Type uint8 } // Error returns message with the invalid type given. func (e *InvalidTypeError) Error() string { return fmt.Sprintf("got invalid type: %v", e.Type) } ================================================ FILE: gtpv1/ie/extended-common-flags-ii.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewExtendedCommonFlagsII creates a new ExtendedCommonFlagsII IE. // // Note: each flag should be set in 1 or 0. func NewExtendedCommonFlagsII(pmtsmi, dtci, pnsi int) *IE { return New( ExtendedCommonFlagsII, []byte{uint8( pmtsmi<<2 | dtci<<1 | pnsi, )}, ) } // ExtendedCommonFlagsII returns ExtendedCommonFlagsII value if type matches. func (i *IE) ExtendedCommonFlagsII() (uint8, error) { if i.Type != ExtendedCommonFlagsII { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustExtendedCommonFlagsII returns ExtendedCommonFlagsII in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustExtendedCommonFlagsII() uint8 { v, _ := i.ExtendedCommonFlagsII() return v } // IsPMTSMI checks if PMTSMI flag exists in ExtendedCommonFlagsII. func (i *IE) IsPMTSMI() bool { return ((i.MustExtendedCommonFlagsII() >> 2) & 0x01) != 0 } // IsDTCI checks if DTCI flag exists in ExtendedCommonFlagsII. func (i *IE) IsDTCI() bool { return ((i.MustExtendedCommonFlagsII() >> 1) & 0x01) != 0 } // IsPNSI checks if PNSI flag exists in ExtendedCommonFlagsII. func (i *IE) IsPNSI() bool { return (i.MustExtendedCommonFlagsII() & 0x01) != 0 } ================================================ FILE: gtpv1/ie/extended-common-flags.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewExtendedCommonFlags creates a new ExtendedCommonFlags IE. // // Note: each flag should be set in 1 or 0. func NewExtendedCommonFlags(uasi, bdwi, pcri, vb, retloc, cpsr, ccrsi, unauthenticatedIMSI int) *IE { return New( ExtendedCommonFlags, []byte{uint8( uasi<<7 | bdwi<<6 | pcri<<5 | vb<<4 | retloc<<3 | cpsr<<2 | ccrsi<<1 | unauthenticatedIMSI, )}, ) } // ExtendedCommonFlags returns ExtendedCommonFlags value if type matches. func (i *IE) ExtendedCommonFlags() (uint8, error) { if i.Type != ExtendedCommonFlags { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustExtendedCommonFlags returns ExtendedCommonFlags in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustExtendedCommonFlags() uint8 { v, _ := i.ExtendedCommonFlags() return v } // IsUASI checks if UASI flag exists in ExtendedCommonFlags. func (i *IE) IsUASI() bool { return ((i.MustExtendedCommonFlags() >> 7) & 0x01) != 0 } // IsBDWI checks if BDWI flag exists in ExtendedCommonFlags. func (i *IE) IsBDWI() bool { return ((i.MustExtendedCommonFlags() >> 6) & 0x01) != 0 } // IsPCRI checks if PCRI flag exists in ExtendedCommonFlags. func (i *IE) IsPCRI() bool { return ((i.MustExtendedCommonFlags() >> 5) & 0x01) != 0 } // IsVB checks if VB flag exists in ExtendedCommonFlags. func (i *IE) IsVB() bool { return ((i.MustExtendedCommonFlags() >> 4) & 0x01) != 0 } // IsRetLoc checks if RetLoc flag exists in ExtendedCommonFlags. func (i *IE) IsRetLoc() bool { return ((i.MustExtendedCommonFlags() >> 3) & 0x01) != 0 } // IsCPSR checks if CPSR flag exists in ExtendedCommonFlags. func (i *IE) IsCPSR() bool { return ((i.MustExtendedCommonFlags() >> 2) & 0x01) != 0 } // IsCCRSI checks if CCRSI flag exists in ExtendedCommonFlags. func (i *IE) IsCCRSI() bool { return ((i.MustExtendedCommonFlags() >> 1) & 0x01) != 0 } // IsUnauthenticatedIMSI checks if UnauthenticatedIMSI flag exists in ExtendedCommonFlags. func (i *IE) IsUnauthenticatedIMSI() bool { return (i.MustExtendedCommonFlags() & 0x01) != 0 } ================================================ FILE: gtpv1/ie/extension-header-type-list.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewExtensionHeaderTypeList creates a new ExtensionHeaderTypeList IE. func NewExtensionHeaderTypeList(types ...uint8) *IE { return New(ExtensionHeaderTypeList, types) } // ExtensionHeaderTypeList returns ExtensionHeaderTypeList in []uint8 if type matches. func (i *IE) ExtensionHeaderTypeList() ([]uint8, error) { if i.Type != ExtensionHeaderTypeList { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustExtensionHeaderTypeList returns ExtensionHeaderTypeList in []uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustExtensionHeaderTypeList() []uint8 { v, _ := i.ExtensionHeaderTypeList() return v } ================================================ FILE: gtpv1/ie/gsn-address.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "net" ) // NewGSNAddress creates a new GSNAddress IE. func NewGSNAddress(addr string) *IE { return NewGSNAddressByIP(net.ParseIP(addr)) } // NewGSNAddressByIP creates a new GSNAddress IE from net.IP. func NewGSNAddressByIP(ip net.IP) *IE { if ip == nil { return nil } v4 := ip.To4() // IPv4 if v4 != nil { return New(GSNAddress, v4) } // IPv6 return New(GSNAddress, ip) } // GSNAddress returns GSNAddress value if type matches. func (i *IE) GSNAddress() (string, error) { if i.Type != GSNAddress { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return "", io.ErrUnexpectedEOF } return net.IP(i.Payload).String(), nil } // MustGSNAddress returns GSNAddress in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustGSNAddress() string { v, _ := i.GSNAddress() return v } ================================================ FILE: gtpv1/ie/ie.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. /* Package ie provides encoding/decoding feature of GTPv1 Information Elements. */ package ie import ( "encoding/binary" "fmt" ) // TV IE definitions. const ( Cause uint8 = 1 IMSI uint8 = 2 RouteingAreaIdentity uint8 = 3 TemporaryLogicalLinkIdentity uint8 = 4 PacketTMSI uint8 = 5 ReorderingRequired uint8 = 8 AuthenticationTriplet uint8 = 9 MAPCause uint8 = 11 PTMSISignature uint8 = 12 MSValidated uint8 = 13 Recovery uint8 = 14 SelectionMode uint8 = 15 TEIDDataI uint8 = 16 TEIDCPlane uint8 = 17 TEIDDataII uint8 = 18 TeardownInd uint8 = 19 NSAPI uint8 = 20 RANAPCause uint8 = 21 RABContext uint8 = 22 RadioPrioritySMS uint8 = 23 RadioPriority uint8 = 24 PacketFlowID uint8 = 25 ChargingCharacteristics uint8 = 26 TraceReference uint8 = 27 TraceType uint8 = 28 MSNotReachableReason uint8 = 29 ChargingID uint8 = 127 ) // TLV IE definitions. const ( EndUserAddress uint8 = 128 MMContext uint8 = 129 PDPContext uint8 = 130 AccessPointName uint8 = 131 ProtocolConfigurationOptions uint8 = 132 GSNAddress uint8 = 133 MSISDN uint8 = 134 QoSProfile uint8 = 135 AuthenticationQuintuplet uint8 = 136 TrafficFlowTemplate uint8 = 137 TargetIdentification uint8 = 138 UTRANTransparentContainer uint8 = 139 RABSetupInformation uint8 = 140 ExtensionHeaderTypeList uint8 = 141 TriggerID uint8 = 142 OMCIdentity uint8 = 143 RANTransparentContainer uint8 = 144 PDPContextPrioritization uint8 = 145 AdditionalRABSetupInformation uint8 = 146 SGSNNumber uint8 = 147 CommonFlags uint8 = 148 APNRestriction uint8 = 149 RadioPriorityLCS uint8 = 150 RATType uint8 = 151 UserLocationInformation uint8 = 152 MSTimeZone uint8 = 153 IMEISV uint8 = 154 CAMELChargingInformationContainer uint8 = 155 MBMSUEContext uint8 = 156 TemporaryMobileGroupIdentity uint8 = 157 RIMRoutingAddress uint8 = 158 MBMSProtocolConfigurationOptions uint8 = 159 MBMSServiceArea uint8 = 160 SourceRNCPDCPContextInfo uint8 = 161 AdditionalTraceInfo uint8 = 162 HopCounter uint8 = 163 SelectedPLMNID uint8 = 164 MBMSSessionIdentifier uint8 = 165 MBMS2G3GIndicator uint8 = 166 EnhancedNSAPI uint8 = 167 MBMSSessionDuration uint8 = 168 AdditionalMBMSTraceInfo uint8 = 169 MBMSSessionRepetitionNumber uint8 = 170 MBMSTimeToDataTransfer uint8 = 171 BSSContainer uint8 = 173 CellIdentification uint8 = 174 PDUNumbers uint8 = 175 BSSGPCause uint8 = 176 RequiredMBMSBearerCapabilities uint8 = 177 RIMRoutingAddressDiscriminator uint8 = 178 ListOfSetupPFCs uint8 = 179 PSHandoverXIDParameters uint8 = 180 MSInfoChangeReportingAction uint8 = 181 DirectTunnelFlags uint8 = 182 CorrelationID uint8 = 183 BearerControlMode uint8 = 184 MBMSFlowIdentifier uint8 = 185 MBMSIPMulticastDistribution uint8 = 186 MBMSDistributionAcknowledgement uint8 = 187 ReliableInterRATHandoverInfo uint8 = 188 RFSPIndex uint8 = 189 FullyQualifiedDomainName uint8 = 190 EvolvedAllocationRetentionPriorityI uint8 = 191 EvolvedAllocationRetentionPriorityII uint8 = 192 ExtendedCommonFlags uint8 = 193 UserCSGInformation uint8 = 194 CSGInformationReportingAction uint8 = 195 CSGID uint8 = 196 CSGMembershipIndication uint8 = 197 AggregateMaximumBitRate uint8 = 198 UENetworkCapability uint8 = 199 UEAMBR uint8 = 200 APNAMBRWithNSAPI uint8 = 201 GGSNBackOffTime uint8 = 202 SignallingPriorityIndication uint8 = 203 SignallingPriorityIndicationWithNSAPI uint8 = 204 HigherBitratesThan16MbpsFlag uint8 = 205 AdditionalMMContextForSRVCC uint8 = 207 AdditionalFlagsForSRVCC uint8 = 208 STNSR uint8 = 209 CMSISDN uint8 = 210 ExtendedRANAPCause uint8 = 211 ENodeBID uint8 = 212 SelectionModeWithNSAPI uint8 = 213 ULITimestamp uint8 = 214 LHNIDWithNSAPI uint8 = 215 CNOperatorSelectionEntity uint8 = 216 UEUsageType uint8 = 217 ExtendedCommonFlagsII uint8 = 218 NodeIdentifier uint8 = 219 CIoTOptimizationsSupportIndication uint8 = 220 SCEFPDNConnection uint8 = 221 IOVUpdatesCounter uint8 = 222 MappedUEUsageType uint8 = 223 UPFunctionSelectionIndicationFlags uint8 = 224 SpecialIETypeForIETypeExtension uint8 = 238 ChargingGatewayAddress uint8 = 251 PrivateExtension uint8 = 255 ) // IE is a GTPv1 Information Element. type IE struct { Type uint8 Length uint16 Payload []byte } // New creates new IE. func New(t uint8, p []byte) *IE { i := &IE{Type: t, Payload: p} i.SetLength() return i } // Marshal returns the byte sequence generated from an IE instance. func (i *IE) Marshal() ([]byte, error) { b := make([]byte, i.MarshalLen()) if err := i.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (i *IE) MarshalTo(b []byte) error { if len(b) < i.MarshalLen() { return ErrTooShortToMarshal } var offset = 1 b[0] = i.Type if i.Type == ExtensionHeaderTypeList { b[1] = uint8(i.Length) offset++ } else if !i.IsTV() { binary.BigEndian.PutUint16(b[1:3], i.Length) offset += 2 } copy(b[offset:i.MarshalLen()], i.Payload) return nil } // Parse decodes given byte sequence as a GTPv1 Information Element. func Parse(b []byte) (*IE, error) { i := &IE{} if err := i.UnmarshalBinary(b); err != nil { return nil, err } return i, nil } // UnmarshalBinary sets the values retrieved from byte sequence in GTPv1 IE. func (i *IE) UnmarshalBinary(b []byte) error { if len(b) < 2 { return ErrTooShortToParse } i.Type = b[0] if i.Type == ExtensionHeaderTypeList { return decodeExtensionHeaderTypeList(i, b) } if i.IsTV() { return decodeTVFromBytes(i, b) } return decodeTLVFromBytes(i, b) } func decodeTVFromBytes(i *IE, b []byte) error { l := len(b) if l < 2 { return ErrTooShortToParse } if i.MarshalLen() > l { return ErrInvalidLength } i.Length = 0 i.Payload = b[1:i.MarshalLen()] return nil } func decodeTLVFromBytes(i *IE, b []byte) error { l := len(b) if l < 3 { return ErrTooShortToParse } i.Length = binary.BigEndian.Uint16(b[1:3]) if int(i.Length)+3 > l { return ErrInvalidLength } i.Payload = b[3 : 3+int(i.Length)] return nil } func decodeExtensionHeaderTypeList(i *IE, b []byte) error { l := len(b) if l < 2 { return ErrTooShortToParse } if i.MarshalLen() > l { return ErrInvalidLength } i.Length = uint16(b[1]) n := 2 + int(i.Length) if n > l { return ErrInvalidLength } i.Payload = b[2:n] return nil } var tvLengthMap = map[int]int{ 0: 0, // Reserved 1: 1, // Cause 2: 8, // IMSI 3: 6, // RouteingAreaIdentity 4: 4, // TLLI 5: 4, // P-TMSI 8: 1, // Reorder Required 9: 28, // Authentication Triplet 11: 1, // MAP Cause 12: 3, // P-TMSI Signature 13: 1, // MS Validated 14: 1, // Recovery 15: 1, // Selection Mode 16: 4, // TEID Data 1 17: 4, // TEID C-Plane 18: 4, // TEID Data 2 19: 1, // Teardown Indication 20: 1, // NSAPI 21: 1, // RANAP Cause 22: 9, // RAB Context 23: 1, // Radio Priority SMS 24: 1, // Radio Priority 25: 2, // Packet Flow ID 26: 2, // Charging Characteristics 27: 2, // Trace Preference 28: 2, // Trace Type 29: 1, // MS Not Reachable Reason 127: 4, // Charging ID } // IsTV checks if a IE is TV format. If false, it indicates the IE has Length inside. func (i *IE) IsTV() bool { return int(i.Type) < 0x80 } // MarshalLen returns the serial length of IE. func (i *IE) MarshalLen() int { if l, ok := tvLengthMap[int(i.Type)]; ok { return l + 1 } if i.Type < 128 { return 1 + len(i.Payload) } if i.Type == ExtensionHeaderTypeList { return 2 + len(i.Payload) } return 3 + len(i.Payload) } // SetLength sets the length in Length field. func (i *IE) SetLength() { if _, ok := tvLengthMap[int(i.Type)]; ok { i.Length = 0 return } i.Length = uint16(len(i.Payload)) } // Name returns the name of IE in string. func (i *IE) Name() string { if n, ok := ieTypeNameMap[i.Type]; ok { return n } return "Undefined" } // String returns the GTPv1 IE values in human readable format. func (i *IE) String() string { if i == nil { return "nil" } return fmt.Sprintf("{%s: {Type: %d, Length: %d, Payload: %#v}}", i.Name(), i.Type, i.Length, i.Payload, ) } // ParseMultiIEs decodes multiple (unspecified number of) IEs to []*IE at a time. func ParseMultiIEs(b []byte) ([]*IE, error) { var ies []*IE for { if len(b) == 0 { break } i, err := Parse(b) if err != nil { return nil, err } ies = append(ies, i) b = b[i.MarshalLen():] continue } return ies, nil } func newUint8ValIE(t, v uint8) *IE { return New(t, []byte{v}) } // left for future use. // func newUint16ValIE(t uint8, v uint16) *IE { // i := New(t, make([]byte, 2)) // binary.BigEndian.PutUint16(i.Payload, v) // return i // } func newUint32ValIE(t uint8, v uint32) *IE { i := New(t, make([]byte, 4)) binary.BigEndian.PutUint32(i.Payload, v) return i } var ieTypeNameMap = map[uint8]string{ 128: "EndUserAddress", 129: "MMContext", 130: "PDPContext", 131: "AccessPointName", 132: "ProtocolConfigurationOptions", 133: "GSNAddress", 134: "MSISDN", 135: "QoSProfile", 136: "AuthenticationQuintuplet", 137: "TrafficFlowTemplate", 138: "TargetIdentification", 139: "UTRANTransparentContainer", 140: "RABSetupInformation", 141: "ExtensionHeaderTypeList", 142: "TriggerID", 143: "OMCIdentity", 144: "RANTransparentContainer", 145: "PDPContextPrioritization", 146: "AdditionalRABSetupInformation", 147: "SGSNNumber", 148: "CommonFlags", 149: "APNRestriction", 150: "RadioPriorityLCS", 151: "RATType", 152: "UserLocationInformation", 153: "MSTimeZone", 154: "IMEISV", 155: "CAMELChargingInformationContainer", 156: "MBMSUEContext", 157: "TemporaryMobileGroupIdentity", 158: "RIMRoutingAddress", 159: "MBMSProtocolConfigurationOptions", 160: "MBMSServiceArea", 161: "SourceRNCPDCPContextInfo", 162: "AdditionalTraceInfo", 163: "HopCounter", 164: "SelectedPLMNID", 165: "MBMSSessionIdentifier", 166: "MBMS2G3GIndicator", 167: "EnhancedNSAPI", 168: "MBMSSessionDuration", 169: "AdditionalMBMSTraceInfo", 170: "MBMSSessionRepetitionNumber", 171: "MBMSTimeToDataTransfer", 173: "BSSContainer", 174: "CellIdentification", 175: "PDUNumbers", 176: "BSSGPCause", 177: "RequiredMBMSBearerCapabilities", 178: "RIMRoutingAddressDiscriminator", 179: "ListOfSetupPFCs", 180: "PSHandoverXIDParameters", 181: "MSInfoChangeReportingAction", 182: "DirectTunnelFlags", 183: "CorrelationID", 184: "BearerControlMode", 185: "MBMSFlowIdentifier", 186: "MBMSIPMulticastDistribution", 187: "MBMSDistributionAcknowledgement", 188: "ReliableInterRATHandoverInfo", 189: "RFSPIndex", 190: "FullyQualifiedDomainName", 191: "EvolvedAllocationRetentionPriorityI", 192: "EvolvedAllocationRetentionPriorityII", 193: "ExtendedCommonFlags", 194: "UserCSGInformation", 195: "CSGInformationReportingAction", 196: "CSGID", 197: "CSGMembershipIndication", 198: "AggregateMaximumBitRate", 199: "UENetworkCapability", 200: "UEAMBR", 201: "APNAMBRWithNSAPI", 202: "GGSNBackOffTime", 203: "SignallingPriorityIndication", 204: "SignallingPriorityIndicationWithNSAPI", 205: "HigherBitratesThan16MbpsFlag", 207: "AdditionalMMContextForSRVCC", 208: "AdditionalFlagsForSRVCC", 209: "STNSR", 210: "CMSISDN", 211: "ExtendedRANAPCause", 212: "ENodeBID", 213: "SelectionModeWithNSAPI", 214: "ULITimestamp", 215: "LHNIDWithNSAPI", 216: "CNOperatorSelectionEntity", 217: "UEUsageType", 218: "ExtendedCommonFlagsII", 219: "NodeIdentifier", 220: "CIoTOptimizationsSupportIndication", 221: "SCEFPDNConnection", 222: "IOVUpdatesCounter", 223: "MappedUEUsageType", 224: "UPFunctionSelectionIndicationFlags", 238: "SpecialIETypeForIETypeExtension", 251: "ChargingGatewayAddress", 255: "PrivateExtension", } ================================================ FILE: gtpv1/ie/ie_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "log" // Serialize serializes IE into bytes. // // Deprecated: use IE.Marshal instead. func (i *IE) Serialize() ([]byte, error) { log.Println("IE.Serialize is deprecated. use IE.Marshal instead") return i.Marshal() } // SerializeTo serializes IE into bytes given as b. // // Deprecated: use IE.MarshalTo instead. func (i *IE) SerializeTo(b []byte) error { log.Println("IE.SerializeTo is deprecated. use IE.MarshalTo instead") return i.MarshalTo(b) } // Decode decodes bytes as IE. // // Deprecated: use Parse instead. func Decode(b []byte) (*IE, error) { log.Println("Decode is deprecated. use Parse instead") return Parse(b) } // DecodeFromBytes decodes bytes as IE. // // Deprecated: use IE.UnmarshalBinary instead. func (i *IE) DecodeFromBytes(b []byte) error { log.Println("IE.DecodeFromBytes is deprecated. use IE.UnmarshalBinary instead") return i.UnmarshalBinary(b) } // Len returns the actual length of IE. // // Deprecated: use IE.MarshalLen instead. func (i *IE) Len() int { log.Println("IE.Len is deprecated. use IE.MarshalLen instead") return i.MarshalLen() } ================================================ FILE: gtpv1/ie/ie_fuzz_test.go ================================================ package ie_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/ie" ) func FuzzParse(f *testing.F) { f.Fuzz(func(t *testing.T, b []byte) { if _, err := ie.Parse(b); err != nil { t.Skip() } }) } ================================================ FILE: gtpv1/ie/ie_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie_test import ( "testing" "time" "github.com/google/go-cmp/cmp" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" ) func TestIEs(t *testing.T) { cases := []struct { description string structured *ie.IE serialized []byte }{ { "IMSI", ie.NewIMSI("123451234567890"), []byte{0x02, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0}, }, { "PacketTMSI", ie.NewPacketTMSI(0xbeebee), []byte{0x05, 0x00, 0xbe, 0xeb, 0xee}, }, { "AuthenticationTriplet", ie.NewAuthenticationTriplet( []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, []byte{0xde, 0xad, 0xbe, 0xef}, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}, ), []byte{ 0x09, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, }, }, { "MAPCause", ie.NewMAPCause(gtpv1.MAPCauseSystemFailure), []byte{0x0b, 0x22}, }, { "PTMSISignature", ie.NewPTMSISignature(0xbeebee), []byte{0x0c, 0xbe, 0xeb, 0xee}, }, { "MSValidated", ie.NewMSValidated(true), []byte{0x0d, 0xff}, }, { "Recovery", ie.NewRecovery(1), []byte{0x0e, 0x01}, }, { "SelectionMode", ie.NewSelectionMode(gtpv1.SelectionModeMSorNetworkProvidedAPNSubscribedVerified), []byte{0x0f, 0xf0}, }, { "TEIDDataI", ie.NewTEIDDataI(0xdeadbeef), []byte{0x10, 0xde, 0xad, 0xbe, 0xef}, }, { "TEIDCPlane", ie.NewTEIDCPlane(0xdeadbeef), []byte{0x11, 0xde, 0xad, 0xbe, 0xef}, }, { "TEIDDataII", ie.NewTEIDDataII(0xdeadbeef), []byte{0x12, 0xde, 0xad, 0xbe, 0xef}, }, { "TeardownInd", ie.NewTeardownInd(true), []byte{0x13, 0xff}, }, { "NSAPI", ie.NewNSAPI(0x05), []byte{0x14, 0x05}, }, { "RANAPCause", ie.NewRANAPCause(gtpv1.MAPCauseUnknownSubscriber), []byte{0x15, 0x01}, }, { "EndUserAddress/v4", ie.NewEndUserAddress("1.1.1.1"), []byte{0x80, 0x00, 0x06, 0xf1, 0x21, 0x01, 0x01, 0x01, 0x01}, }, { "EndUserAddress/v6", ie.NewEndUserAddress("2001::1"), []byte{ 0x80, 0x00, 0x12, 0xf1, 0x57, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }, }, { "AccessPointName", ie.NewAccessPointName("some.apn.example"), []byte{ 0x83, 0x00, 0x11, 0x04, 0x73, 0x6f, 0x6d, 0x65, 0x03, 0x61, 0x70, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, }, }, { "GSNAddressV4", ie.NewGSNAddress("1.1.1.1"), []byte{0x85, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01}, }, { "GSNAddressV6", ie.NewGSNAddress("2001::1"), []byte{ 0x85, 0x00, 0x10, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }, }, { "MSISDN", ie.NewMSISDN("818012345678"), []byte{0x86, 0x00, 0x07, 0x91, 0x18, 0x08, 0x21, 0x43, 0x65, 0x87}, }, { "AuthenticationQuintuplet", ie.NewAuthenticationQuintuplet( []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, ), []byte{ 0x88, 0x00, 0x52, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x10, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x10, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, }, }, { "ExtensionHeaderTypeList", ie.NewExtensionHeaderTypeList( message.ExtHeaderTypePDUSessionContainer, message.ExtHeaderTypeUDPPort, ), []byte{0x8d, 0x02, 0x85, 0x40}, }, { "CommonFlags", ie.NewCommonFlags(0, 1, 0, 0, 0, 0, 0, 0), []byte{0x94, 0x00, 0x01, 0x40}, }, { "APNRestriction", ie.NewAPNRestriction(gtpv1.APNRestrictionPrivate1), []byte{0x95, 0x00, 0x01, 0x03}, }, { "RATType", ie.NewRATType(gtpv1.RatTypeEUTRAN), []byte{0x97, 0x00, 0x01, 0x06}, }, { "UserLocationInformationWithCGI", ie.NewUserLocationInformationWithCGI("123", "45", 0xff, 0), []byte{0x98, 0x00, 0x08, 0x00, 0x21, 0xf3, 0x54, 0x00, 0xff, 0x00, 0x00}, }, { "UserLocationInformationWithSAI", ie.NewUserLocationInformationWithSAI("123", "45", 0xff, 0), []byte{0x98, 0x00, 0x08, 0x01, 0x21, 0xf3, 0x54, 0x00, 0xff, 0x00, 0x00}, }, { "UserLocationInformationWithRAI", ie.NewUserLocationInformationWithRAI("123", "45", 0xff, 0), []byte{0x98, 0x00, 0x07, 0x02, 0x21, 0xf3, 0x54, 0x00, 0xff, 0x00}, }, { "MSTimeZone", ie.NewMSTimeZone(9*time.Hour, 0), // XXX - should be updated with more realistic value []byte{0x99, 0x00, 0x02, 0x63, 0x00}, }, { "MSTimeZone", ie.NewMSTimeZone(2*time.Hour, 0), []byte{0x99, 0x00, 0x02, 0x80, 0x00}, }, { "IMEISV", ie.NewIMEISV("123450123456789"), []byte{0x9a, 0x00, 0x08, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9}, }, { "ULITimestamp", ie.NewULITimestamp(time.Date(2019, time.January, 1, 0, 0, 0, 0, time.UTC)), []byte{0xd6, 0x00, 0x04, 0xdf, 0xd5, 0x2c, 0x00}, }, { "ChargingID", ie.NewChargingID(0xffffffff), []byte{0x7f, 0xff, 0xff, 0xff, 0xff}, }, { "PrivateExtension", ie.NewPrivateExtension(0x0080, []byte{0xde, 0xad, 0xbe, 0xef}), []byte{ // Type, Length 0xff, 0x00, 0x06, // Value 0x00, 0x80, 0xde, 0xad, 0xbe, 0xef, }, }, } for _, c := range cases { t.Run("Marshal/"+c.description, func(t *testing.T) { got, err := c.structured.Marshal() if err != nil { t.Fatal(err) } if diff := cmp.Diff(got, c.serialized); diff != "" { t.Error(diff) } }) t.Run("Parse/"+c.description, func(t *testing.T) { got, err := ie.Parse(c.serialized) if err != nil { t.Fatal(err) } opt := cmp.AllowUnexported(*got, *c.structured) if diff := cmp.Diff(got, c.structured, opt); diff != "" { t.Error(diff) } }) } } ================================================ FILE: gtpv1/ie/imei.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "strings" "github.com/wmnsk/go-gtp/utils" ) // NewIMEISV creates a new IMEISV IE. func NewIMEISV(imei string) *IE { i, err := utils.StrToSwappedBytes(imei, "f") if err != nil { return nil } return New(IMEISV, i) } // IMEISV returns IMEISV value if type matches. func (i *IE) IMEISV() (string, error) { if i.Type != IMEISV { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return "", io.ErrUnexpectedEOF } str := utils.SwappedBytesToStr(i.Payload, false) return strings.TrimSuffix(str, "f"), nil } // MustIMEISV returns IMEISV in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustIMEISV() string { v, _ := i.IMEISV() return v } ================================================ FILE: gtpv1/ie/imei_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "testing" func TestIE_IMEISV(t *testing.T) { t.Run("Encode/Decode IMEI", func(t *testing.T) { ie := NewIMEISV("123456789012345") got := ie.MustIMEISV() if got != "123456789012345" { t.Errorf("wrong IMEI, got: %v", got) } }) t.Run("Encode/Decode IMEISV", func(t *testing.T) { ie := NewIMEISV("1234567890123456") got := ie.MustIMEISV() if got != "1234567890123456" { t.Errorf("wrong IMEISV, got: %v", got) } }) } ================================================ FILE: gtpv1/ie/imsi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewIMSI creates a new IMSI IE. func NewIMSI(imsi string) *IE { i, err := utils.StrToSwappedBytes(imsi, "f") if err != nil { return nil } return New(IMSI, i) } // IMSI returns IMSI value if type matches. func (i *IE) IMSI() (string, error) { if i.Type != IMSI { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return "", io.ErrUnexpectedEOF } return utils.SwappedBytesToStr(i.Payload, true), nil } // MustIMSI returns IMSI in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustIMSI() string { v, _ := i.IMSI() return v } ================================================ FILE: gtpv1/ie/ip.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "net" ) // IP returns IP in net.IP if type matches. func (i *IE) IP() (net.IP, error) { if len(i.Payload) < 4 { return nil, io.ErrUnexpectedEOF } switch i.Type { case EndUserAddress: if i.MustPDPTypeOrganization() != pdpTypeIETF { return nil, ErrMalformed } return net.IP(i.Payload[2:]), nil case GSNAddress: return net.IP(i.Payload), nil default: return nil, &InvalidTypeError{i.Type} } } func (i *IE) MustIP() net.IP { v, _ := i.IP() return v } // IPAddress returns IPAddress in string if type matches. func (i *IE) IPAddress() (string, error) { ip, err := i.IP() if err != nil { return "", err } return ip.String(), nil } // MustIPAddress returns IPAddress in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustIPAddress() string { v, _ := i.IPAddress() return v } ================================================ FILE: gtpv1/ie/lac.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // LAC returns LAC value if type matches. func (i *IE) LAC() (uint16, error) { switch i.Type { case RouteingAreaIdentity: if len(i.Payload) < 5 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[3:5]), nil case UserLocationInformation: if len(i.Payload) < 6 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[4:6]), nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustLAC returns LAC in uint16 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustLAC() uint16 { v, _ := i.LAC() return v } ================================================ FILE: gtpv1/ie/map-cause.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewMAPCause creates a new MAPCause IE. func NewMAPCause(cause uint8) *IE { return newUint8ValIE(MAPCause, cause) } // MAPCause returns MAPCause in uint8 if type matches. func (i *IE) MAPCause() (uint8, error) { if i.Type != MAPCause { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustMAPCause returns MAPCause in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustMAPCause() uint8 { v, _ := i.MAPCause() return v } ================================================ FILE: gtpv1/ie/mcc-mnc.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // MCC returns MCC value if type matches. func (i *IE) MCC() (string, error) { switch i.Type { case RouteingAreaIdentity: if len(i.Payload) < 2 { return "", io.ErrUnexpectedEOF } return utils.DecodeMCC(i.Payload[0:2]), nil case UserLocationInformation: if len(i.Payload) < 3 { return "", io.ErrUnexpectedEOF } return utils.DecodeMCC(i.Payload[1:3]), nil default: return "", &InvalidTypeError{Type: i.Type} } } // MustMCC returns MCC in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustMCC() string { v, _ := i.MCC() return v } // MNC returns MNC value if type matches. func (i *IE) MNC() (string, error) { switch i.Type { case RouteingAreaIdentity: if len(i.Payload) < 3 { return "", io.ErrUnexpectedEOF } return utils.DecodeMNC(i.Payload[1:3]), nil case UserLocationInformation: if len(i.Payload) < 4 { return "", io.ErrUnexpectedEOF } return utils.DecodeMNC(i.Payload[2:4]), nil default: return "", &InvalidTypeError{Type: i.Type} } } // MustMNC returns MNC in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustMNC() string { v, _ := i.MNC() return v } ================================================ FILE: gtpv1/ie/ms-timezone.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "math" "time" ) // Timezone adjustment definitions. const ( TimeAdjustNoDaylightSaving uint8 = iota TimeAdjustDaylightSavingOneHour TimeAdjustDaylightSavingTwoHour ) // NewMSTimeZone creates a new MSTimeZone IE. func NewMSTimeZone(tz time.Duration, daylightSaving uint8) *IE { i := New(MSTimeZone, make([]byte, 2)) min := tz.Minutes() / 15 absMin := int(math.Abs(min)) hex := byte(((absMin % 10) << 4) | (absMin / 10)) if min < 0 { hex |= 0x08 } i.Payload[0] = hex i.Payload[1] = daylightSaving & 0x03 return i } // TimeZone returns TimeZone in time.Duration if the type of IE matches. func (i *IE) TimeZone() (time.Duration, error) { if i.Type != MSTimeZone { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } unsigned := i.Payload[0] & 0xf7 dec := int((unsigned >> 4) + (unsigned&0x0f)*10) if (i.Payload[0]&0x08)>>3 == 1 { dec *= -1 } return time.Duration(dec*15) * time.Minute, nil } // MustTimeZone returns TimeZone in time.Duration if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustTimeZone() time.Duration { v, _ := i.TimeZone() return v } // DaylightSaving returns DaylightSaving in uint8 if the type of IE matches. func (i *IE) DaylightSaving() (uint8, error) { if i.Type != MSTimeZone { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return i.Payload[1], nil } // MustDaylightSaving returns DaylightSaving in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustDaylightSaving() uint8 { v, _ := i.DaylightSaving() return v } ================================================ FILE: gtpv1/ie/ms-validated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewMSValidated creates a new MSValidated IE. func NewMSValidated(validated bool) *IE { if validated { return newUint8ValIE(MSValidated, 0xff) } return newUint8ValIE(MSValidated, 0xfe) } // MSValidated returns MSValidated in bool if type matches. func (i *IE) MSValidated() bool { if i.Type != MSValidated { return false } if len(i.Payload) == 0 { return false } return i.Payload[0]%2 == 1 } ================================================ FILE: gtpv1/ie/msisdn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewMSISDN creates a new MSISDN IE. func NewMSISDN(msisdn string) *IE { i, err := utils.StrToSwappedBytes("19"+msisdn, "f") if err != nil { return nil } return New(MSISDN, i) } // MSISDN returns MSISDN value if type matches. func (i *IE) MSISDN() (string, error) { if i.Type != MSISDN { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return "", io.ErrUnexpectedEOF } return utils.SwappedBytesToStr(i.Payload[1:], false), nil } // MustMSISDN returns MSISDN in string if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustMSISDN() string { v, _ := i.MSISDN() return v } ================================================ FILE: gtpv1/ie/nsapi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewNSAPI creates a new NSAPI IE. func NewNSAPI(nsapi uint8) *IE { return newUint8ValIE(NSAPI, nsapi) } // NSAPI returns NSAPI value if type matches. func (i *IE) NSAPI() (uint8, error) { if i.Type != NSAPI { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustNSAPI returns NSAPI in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustNSAPI() uint8 { v, _ := i.NSAPI() return v } ================================================ FILE: gtpv1/ie/p-tmsi-signature.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewPTMSISignature creates a new PTMSISignature IE. func NewPTMSISignature(sig uint32) *IE { return New(PTMSISignature, utils.Uint32To24(sig)) } // PTMSISignature returns PTMSISignature value in uint32 if type matches. func (i *IE) PTMSISignature() (uint32, error) { if i.Type != PTMSISignature { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 3 { return 0, io.ErrUnexpectedEOF } return utils.Uint24To32(i.Payload), nil } // MustPTMSISignature returns PTMSISignature in uint32 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPTMSISignature() uint32 { v, _ := i.PTMSISignature() return v } ================================================ FILE: gtpv1/ie/p-tmsi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewPacketTMSI creates a new PacketTMSI IE. func NewPacketTMSI(ptmsi uint32) *IE { return newUint32ValIE(PacketTMSI, ptmsi) } // PacketTMSI returns PacketTMSI value in uint32 if type matches. func (i *IE) PacketTMSI() (uint32, error) { if i.Type != PacketTMSI { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload), nil } // MustPacketTMSI returns PacketTMSI in uint32 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPacketTMSI() uint32 { v, _ := i.PacketTMSI() return v } ================================================ FILE: gtpv1/ie/pco.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "encoding/binary" // ConfigurationProtocolOption represents a Configuration protocol option in PCO. type ConfigurationProtocolOption struct { ProtocolID uint16 Length uint8 Contents []byte } // NewConfigurationProtocolOption creates a new ConfigurationProtocolOption. func NewConfigurationProtocolOption(pid uint16, contents []byte) *ConfigurationProtocolOption { c := &ConfigurationProtocolOption{ ProtocolID: pid, Length: uint8(len(contents)), Contents: contents, } return c } // Marshal serializes ConfigurationProtocolOption. func (c *ConfigurationProtocolOption) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ConfigurationProtocolOption. func (c *ConfigurationProtocolOption) MarshalTo(b []byte) error { binary.BigEndian.PutUint16(b[0:2], c.ProtocolID) b[2] = c.Length if c.Length != 0 { copy(b[3:], c.Contents) } return nil } // ParseConfigurationProtocolOption decodes ConfigurationProtocolOption. func ParseConfigurationProtocolOption(b []byte) (*ConfigurationProtocolOption, error) { c := &ConfigurationProtocolOption{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes into ConfigurationProtocolOption. func (c *ConfigurationProtocolOption) UnmarshalBinary(b []byte) error { l := len(b) if l < 3 { return ErrTooShortToParse } c.ProtocolID = binary.BigEndian.Uint16(b[0:2]) c.Length = b[2] if c.Length != 0 && l >= 3+int(c.Length) { c.Contents = make([]byte, c.Length) copy(c.Contents, b[3:3+int(c.Length)]) } return nil } // MarshalLen returns the serial length of ConfigurationProtocolOption in int. func (c *ConfigurationProtocolOption) MarshalLen() int { return 3 + len(c.Contents) } // PCOPayload is a Payload of ProtocolConfigurationPayload IE. type PCOPayload struct { ConfigurationProtocol uint8 ConfigurationProtocolOptions []*ConfigurationProtocolOption } // NewPCOPayload creates a new PCOPayload. func NewPCOPayload(configProto uint8, opts ...*ConfigurationProtocolOption) *PCOPayload { p := &PCOPayload{ConfigurationProtocol: configProto} p.ConfigurationProtocolOptions = append(p.ConfigurationProtocolOptions, opts...) return p } // Marshal serializes PCOPayload. func (p *PCOPayload) Marshal() ([]byte, error) { b := make([]byte, p.MarshalLen()) if err := p.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes PCOPayload. func (p *PCOPayload) MarshalTo(b []byte) error { b[0] = (p.ConfigurationProtocol & 0x07) | 0x80 offset := 1 for _, opt := range p.ConfigurationProtocolOptions { if err := opt.MarshalTo(b[offset:]); err != nil { return err } offset += opt.MarshalLen() } return nil } // ParsePCOPayload decodes PCOPayload. func ParsePCOPayload(b []byte) (*PCOPayload, error) { p := &PCOPayload{} if err := p.UnmarshalBinary(b); err != nil { return nil, err } return p, nil } // UnmarshalBinary decodes given bytes into PCOPayload. func (p *PCOPayload) UnmarshalBinary(b []byte) error { if len(b) == 0 { return ErrTooShortToParse } p.ConfigurationProtocol = b[0] & 0x07 offset := 1 for { if offset >= len(b) { return nil } opt, err := ParseConfigurationProtocolOption(b[offset:]) if err != nil { return err } p.ConfigurationProtocolOptions = append(p.ConfigurationProtocolOptions, opt) offset += opt.MarshalLen() } } // MarshalLen returns the serial length of PCOPayload in int. func (p *PCOPayload) MarshalLen() int { l := 1 for _, opt := range p.ConfigurationProtocolOptions { l += opt.MarshalLen() } return l } // NewProtocolConfigurationOptions creates a new ProtocolConfigurationOptions IE. func NewProtocolConfigurationOptions(configProto uint8, options ...*ConfigurationProtocolOption) *IE { pco := NewPCOPayload(configProto, options...) i := New(ProtocolConfigurationOptions, make([]byte, pco.MarshalLen())) if err := pco.MarshalTo(i.Payload); err != nil { return nil } return i } // ProtocolConfigurationOptions returns ProtocolConfigurationOptions in // PCOPayload type if the type of IE matches. func (i *IE) ProtocolConfigurationOptions() (*PCOPayload, error) { if i.Type != ProtocolConfigurationOptions { return nil, &InvalidTypeError{Type: i.Type} } pco, err := ParsePCOPayload(i.Payload) if err != nil { return nil, err } return pco, nil } // MustProtocolConfigurationOptions returns ProtocolConfigurationOptions in uint32 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustProtocolConfigurationOptions() *PCOPayload { v, _ := i.ProtocolConfigurationOptions() return v } ================================================ FILE: gtpv1/ie/private-extension.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewPrivateExtension creates a new PrivateExtension IE from string. func NewPrivateExtension(id uint16, val []byte) *IE { i := New(PrivateExtension, make([]byte, 2+len(val))) binary.BigEndian.PutUint16(i.Payload[:2], id) copy(i.Payload[2:], val) return i } // PrivateExtension returns PrivateExtension value if type matches. func (i *IE) PrivateExtension() ([]byte, error) { if i.Type != PrivateExtension { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustPrivateExtension returns PrivateExtension in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustPrivateExtension() []byte { v, _ := i.PrivateExtension() return v } // ExtensionIdentifier returns ExtensionIdentifier value in uint16 if type matches. func (i *IE) ExtensionIdentifier() (uint16, error) { if i.Type != PrivateExtension { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[:2]), nil } // MustExtensionIdentifier returns ExtensionIdentifier in uint16 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustExtensionIdentifier() uint16 { v, _ := i.ExtensionIdentifier() return v } // ExtensionValue returns ExtensionValue value if type matches. func (i *IE) ExtensionValue() ([]byte, error) { if i.Type != PrivateExtension { return nil, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 3 { return nil, io.ErrUnexpectedEOF } return i.Payload[2:], nil } // MustExtensionValue returns ExtensionValue in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustExtensionValue() []byte { v, _ := i.ExtensionValue() return v } ================================================ FILE: gtpv1/ie/qos-profile.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewQoSProfile creates a new QoSProfile IE. // // XXX - NOT Fully implemented. Users need to put the whole payload in []byte. func NewQoSProfile(payload []byte) *IE { return New(QoSProfile, payload) } // QoSProfile returns QoSProfile if type matches. // // XXX - NOT Fully implemented. This method just returns the whole payload in []byte. func (i *IE) QoSProfile() ([]byte, error) { if i.Type != QoSProfile { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustQoSProfile returns QoSProfile in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustQoSProfile() []byte { v, _ := i.QoSProfile() return v } ================================================ FILE: gtpv1/ie/rac.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // RAC returns RAC value if type matches. func (i *IE) RAC() (uint8, error) { switch i.Type { case RouteingAreaIdentity: if len(i.Payload) < 6 { return 0, io.ErrUnexpectedEOF } return i.Payload[5], nil case UserLocationInformation: if len(i.Payload) < 7 { return 0, io.ErrUnexpectedEOF } if i.Payload[0] == locTypeRAI { return i.Payload[6], nil } } return 0, &InvalidTypeError{Type: i.Type} } // MustRAC returns RAC in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustRAC() uint8 { v, _ := i.RAC() return v } ================================================ FILE: gtpv1/ie/rai.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "github.com/wmnsk/go-gtp/utils" ) // NewRouteingAreaIdentity creates a new RouteingAreaIdentity IE. func NewRouteingAreaIdentity(mcc, mnc string, lac uint16, rac uint8) *IE { mc, err := utils.StrToSwappedBytes(mcc, "f") if err != nil { return nil } mn, err := utils.StrToSwappedBytes(mnc, "f") if err != nil { return nil } rai := New( RouteingAreaIdentity, make([]byte, 6), ) copy(rai.Payload[0:2], mc) rai.Payload[2] = mn[0] binary.BigEndian.PutUint16(rai.Payload[3:5], lac) rai.Payload[5] = rac return rai } // RouteingAreaIdentity returns RouteingAreaIdentity value if type matches. func (i *IE) RouteingAreaIdentity() ([]byte, error) { if i.Type != RouteingAreaIdentity { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustRouteingAreaIdentity returns RouteingAreaIdentity in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustRouteingAreaIdentity() []byte { v, _ := i.RouteingAreaIdentity() return v } ================================================ FILE: gtpv1/ie/rai_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "testing" ) func TestRouteingAreaIdentity(t *testing.T) { t.Run("Routeing Area Identity", func(t *testing.T) { ie := NewRouteingAreaIdentity("123", "45", 111, 222) rac := ie.MustRAC() if rac != 222 { t.Errorf("wrong rac, got %v", rac) } lac := ie.MustLAC() if lac != 111 { t.Errorf("wrong lac, got %v", lac) } mcc := ie.MustMCC() if mcc != "123" { t.Errorf("wrong mcc, got %v", mcc) } mnc := ie.MustMNC() if mnc != "45" { t.Errorf("wrong mnc, got %v", mnc) } }) } ================================================ FILE: gtpv1/ie/ranap-cause.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewRANAPCause creates a new RANAPCause IE. func NewRANAPCause(cause uint8) *IE { return newUint8ValIE(RANAPCause, cause) } // RANAPCause returns RANAPCause in uint8 if type matches. func (i *IE) RANAPCause() (uint8, error) { if i.Type != RANAPCause { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustRANAPCause returns RANAPCause in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustRANAPCause() uint8 { v, _ := i.RANAPCause() return v } ================================================ FILE: gtpv1/ie/rat-type.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewRATType creates a new RATType IE. func NewRATType(ratType uint8) *IE { return New( RATType, []byte{ratType}, ) } // RATType returns RATType value if type matches. func (i *IE) RATType() (uint8, error) { if i.Type != RATType { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustRATType returns RATType in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustRATType() uint8 { v, _ := i.RATType() return v } ================================================ FILE: gtpv1/ie/recovery.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewRecovery creates a new Recovery IE. func NewRecovery(recovery uint8) *IE { return newUint8ValIE(Recovery, recovery) } // Recovery returns Recovery value if type matches. func (i *IE) Recovery() (uint8, error) { if i.Type != Recovery { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustRecovery returns Recovery in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustRecovery() uint8 { v, _ := i.Recovery() return v } ================================================ FILE: gtpv1/ie/reordering-required.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewReorderingRequired creates a new ReorderingRequired IE. func NewReorderingRequired(required bool) *IE { if required { return newUint8ValIE(ReorderingRequired, 0xff) } return newUint8ValIE(ReorderingRequired, 0xfe) } // ReorderingRequired returns ReorderingRequired or not if type matches. func (i *IE) ReorderingRequired() bool { if i.Type != ReorderingRequired { return false } if len(i.Payload) == 0 { return false } return i.Payload[0]&0x01 == 1 } ================================================ FILE: gtpv1/ie/selection-mode.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewSelectionMode creates a new SelectionMode IE. // Note that exactly one of the parameters should be set to true. // Otherwise, you'll get the unexpected result. func NewSelectionMode(mode uint8) *IE { return newUint8ValIE(SelectionMode, mode) } // SelectionMode returns SelectionMode value if type matches. func (i *IE) SelectionMode() (uint8, error) { if i.Type != SelectionMode { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) == 0 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // MustSelectionMode returns SelectionMode in uint8 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustSelectionMode() uint8 { v, _ := i.SelectionMode() return v } ================================================ FILE: gtpv1/ie/teardown-ind.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewTeardownInd creates a new TeardownInd IE. func NewTeardownInd(teardown bool) *IE { if teardown { return newUint8ValIE(TeardownInd, 0xff) } return newUint8ValIE(TeardownInd, 0xfe) } // TeardownInd returns TeardownInd in bool if type matches. func (i *IE) TeardownInd() bool { if i.Type != TeardownInd { return false } if len(i.Payload) == 0 { return false } return i.Payload[0]%2 == 1 } ================================================ FILE: gtpv1/ie/teid.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewTEIDDataI creates a new TEIDDataI IE. func NewTEIDDataI(teid uint32) *IE { return newUint32ValIE(TEIDDataI, teid) } // NewTEIDCPlane creates a new TEID C-Plane IE. func NewTEIDCPlane(teid uint32) *IE { return newUint32ValIE(TEIDCPlane, teid) } // NewTEIDDataII creates a new TEIDDataII IE. func NewTEIDDataII(teid uint32) *IE { return newUint32ValIE(TEIDDataII, teid) } // TEID returns TEID value if type matches. func (i *IE) TEID() (uint32, error) { if len(i.Payload) < 4 { return 0, io.ErrUnexpectedEOF } switch i.Type { case TEIDCPlane, TEIDDataI, TEIDDataII: return binary.BigEndian.Uint32(i.Payload), nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustTEID returns TEID in uint32 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustTEID() uint32 { v, _ := i.TEID() return v } ================================================ FILE: gtpv1/ie/uli-timestamp.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "time" ) // NewULITimestamp creates a new ULITimestamp IE. func NewULITimestamp(ts time.Time) *IE { u64sec := uint64(ts.Sub(time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC))) / 1000000000 return newUint32ValIE(ULITimestamp, uint32(u64sec)) } // Timestamp returns Timestamp in time.Time if the type of IE matches. func (i *IE) Timestamp() (time.Time, error) { if len(i.Payload) < 4 { return time.Time{}, io.ErrUnexpectedEOF } switch i.Type { case ULITimestamp: return time.Unix(int64(binary.BigEndian.Uint32(i.Payload)-2208988800), 0), nil default: return time.Time{}, &InvalidTypeError{Type: i.Type} } } // MustTimestamp returns Timestamp in time.Time if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustTimestamp() time.Time { v, _ := i.Timestamp() return v } ================================================ FILE: gtpv1/ie/uli.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "github.com/wmnsk/go-gtp/utils" ) // UserLocationInformation GeographicLocationType definitions. const ( locTypeCGI uint8 = iota locTypeSAI locTypeRAI ) // NewUserLocationInformationWithCGI creates a new UserLocationInformation IE with LAC. func NewUserLocationInformationWithCGI(mcc, mnc string, lac, cgi uint16) *IE { plmn, err := utils.EncodePLMN(mcc, mnc) if err != nil { return nil } uli := New( UserLocationInformation, make([]byte, 8), ) uli.Payload[0] = locTypeCGI copy(uli.Payload[1:4], plmn) binary.BigEndian.PutUint16(uli.Payload[4:6], lac) binary.BigEndian.PutUint16(uli.Payload[6:8], cgi) return uli } // NewUserLocationInformationWithSAI creates a new UserLocationInformation IE with LAC. func NewUserLocationInformationWithSAI(mcc, mnc string, lac, sac uint16) *IE { plmn, err := utils.EncodePLMN(mcc, mnc) if err != nil { return nil } uli := New( UserLocationInformation, make([]byte, 8), ) uli.Payload[0] = locTypeSAI copy(uli.Payload[1:4], plmn) binary.BigEndian.PutUint16(uli.Payload[4:6], lac) binary.BigEndian.PutUint16(uli.Payload[6:8], sac) return uli } // NewUserLocationInformationWithRAI creates a new UserLocationInformation IE with LAC. func NewUserLocationInformationWithRAI(mcc, mnc string, lac uint16, rac uint8) *IE { plmn, err := utils.EncodePLMN(mcc, mnc) if err != nil { return nil } uli := New( UserLocationInformation, make([]byte, 7), ) uli.Payload[0] = locTypeRAI copy(uli.Payload[1:4], plmn) binary.BigEndian.PutUint16(uli.Payload[4:6], lac) uli.Payload[6] = rac return uli } // UserLocationInformation returns UserLocationInformation value if type matches. func (i *IE) UserLocationInformation() ([]byte, error) { if i.Type != UserLocationInformation { return nil, &InvalidTypeError{Type: i.Type} } return i.Payload, nil } // MustUserLocationInformation returns UserLocationInformation in []byte if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustUserLocationInformation() []byte { v, _ := i.UserLocationInformation() return v } // CGI returns CGI value if type matches. func (i *IE) CGI() (uint16, error) { if i.Type != UserLocationInformation { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } if i.Payload[0] == locTypeCGI { if len(i.Payload) < 8 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[6:8]), nil } return 0, &InvalidTypeError{Type: i.Type} } // MustCGI returns CGI in uint16 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustCGI() uint16 { v, _ := i.CGI() return v } // SAC returns SAC value if type matches. func (i *IE) SAC() (uint16, error) { if i.Type != UserLocationInformation { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } if i.Payload[0] == locTypeSAI { if len(i.Payload) < 8 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[6:8]), nil } return 0, &InvalidTypeError{Type: i.Type} } // MustSAC returns SAC in uint16 if type matches. // This should only be used if it is assured to have the value. func (i *IE) MustSAC() uint16 { v, _ := i.SAC() return v } ================================================ FILE: gtpv1/ie/uli_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "testing" ) func TestUserLocationInformationWithCGI(t *testing.T) { t.Run("Test UserLocationInformation with CGI", func(t *testing.T) { ie := NewUserLocationInformationWithCGI("123", "45", 111, 222) cgi := ie.MustCGI() if cgi != 222 { t.Errorf("wrong cgi, got %v", cgi) } lac := ie.MustLAC() if lac != 111 { t.Errorf("wrong lac, got %v", lac) } mcc := ie.MustMCC() if mcc != "123" { t.Errorf("wrong mcc, got %v", mcc) } mnc := ie.MustMNC() if mnc != "45" { t.Errorf("wrong mnc, got %v", mnc) } }) t.Run("Test UserLocationInformation with CGI (3-digit MNC)", func(t *testing.T) { ie := NewUserLocationInformationWithCGI("123", "456", 111, 222) cgi := ie.MustCGI() if cgi != 222 { t.Errorf("wrong cgi, got %v", cgi) } lac := ie.MustLAC() if lac != 111 { t.Errorf("wrong lac, got %v", lac) } mcc := ie.MustMCC() if mcc != "123" { t.Errorf("wrong mcc, got %v", mcc) } mnc := ie.MustMNC() if mnc != "456" { t.Errorf("wrong mnc, got %v", mnc) } }) } func TestUserLocationInformationWithRAI(t *testing.T) { t.Run("Test UserLocationInformation with RAI", func(t *testing.T) { ie := NewUserLocationInformationWithRAI("123", "45", 111, 222) rac := ie.MustRAC() if rac != 222 { t.Errorf("wrong rac, got %v", rac) } lac := ie.MustLAC() if lac != 111 { t.Errorf("wrong lac, got %v", lac) } mcc := ie.MustMCC() if mcc != "123" { t.Errorf("wrong mcc, got %v", mcc) } mnc := ie.MustMNC() if mnc != "45" { t.Errorf("wrong mnc, got %v", mnc) } }) } func TestUserLocationInformationWithSAI(t *testing.T) { t.Run("Test UserLocationInformation with SAI", func(t *testing.T) { ie := NewUserLocationInformationWithSAI("123", "45", 111, 222) sac := ie.MustSAC() if sac != 222 { t.Errorf("wrong sac, got %v", sac) } lac := ie.MustLAC() if lac != 111 { t.Errorf("wrong lac, got %v", lac) } mcc := ie.MustMCC() if mcc != "123" { t.Errorf("wrong mcc, got %v", mcc) } mnc := ie.MustMNC() if mnc != "45" { t.Errorf("wrong mnc, got %v", mnc) } }) } ================================================ FILE: gtpv1/logger.go ================================================ // Copyright 2019 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 import ( "io" "log" "os" "sync" ) var ( logger = log.New(os.Stderr, "", log.LstdFlags) logMu sync.Mutex ) // SetLogger replaces the standard logger with arbitrary *log.Logger. // // This package prints just informational logs from goroutines working background // that might help developers test the program but can be ignored safely. More // important ones that needs any action by caller would be returned as errors. func SetLogger(l *log.Logger) { if l == nil { log.Println("Don't pass nil to SetLogger: use DisableLogging instead.") } setLogger(l) } // EnableLogging enables the logging from the package. // If l is nil, it uses default logger provided by the package. // Logging is enabled by default. // // See also: SetLogger. func EnableLogging(l *log.Logger) { logMu.Lock() defer logMu.Unlock() setLogger(l) } // DisableLogging disables the logging from the package. // Logging is enabled by default. func DisableLogging() { logMu.Lock() defer logMu.Unlock() logger.SetOutput(io.Discard) } func setLogger(l *log.Logger) { if l == nil { l = log.New(os.Stderr, "", log.LstdFlags) } logMu.Lock() defer logMu.Unlock() logger = l } func logf(format string, v ...interface{}) { logMu.Lock() defer logMu.Unlock() logger.Printf(format, v...) } ================================================ FILE: gtpv1/message/create-pdp-context-req.go ================================================ // Copyright 2019-2023 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // CreatePDPContextRequest is a CreatePDPContextRequest Header and its IEs above. type CreatePDPContextRequest struct { *Header IMSI *ie.IE RAI *ie.IE Recovery *ie.IE SelectionMode *ie.IE TEIDDataI *ie.IE TEIDCPlane *ie.IE NSAPI *ie.IE LinkedNSAPI *ie.IE ChargingCharacteristics *ie.IE TraceReference *ie.IE TraceType *ie.IE EndUserAddress *ie.IE APN *ie.IE PCO *ie.IE SGSNAddressForSignalling *ie.IE SGSNAddressForUserTraffic *ie.IE MSISDN *ie.IE QoSProfile *ie.IE TFT *ie.IE TriggerID *ie.IE OMCIdentity *ie.IE CommonFlags *ie.IE APNRestriction *ie.IE RATType *ie.IE UserLocationInformation *ie.IE MSTimeZone *ie.IE IMEI *ie.IE CAMELChargingInformationContainer *ie.IE AdditionalTraceInfo *ie.IE CorrelationID *ie.IE EvolvedARPI *ie.IE ExtendedCommonFlags *ie.IE UCI *ie.IE APNAMBR *ie.IE SignallingPriorityIndication *ie.IE CNOperatorSelectionEntity *ie.IE MappedUEUsageType *ie.IE UPFunctionSelectionIndicationFlags *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewCreatePDPContextRequest creates a new GTPv1 CreatePDPContextRequest. func NewCreatePDPContextRequest(teid uint32, seq uint16, ies ...*ie.IE) *CreatePDPContextRequest { c := &CreatePDPContextRequest{ Header: NewHeader(0x32, MsgTypeCreatePDPContextRequest, teid, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.RouteingAreaIdentity: c.RAI = i case ie.Recovery: c.Recovery = i case ie.SelectionMode: c.SelectionMode = i case ie.TEIDDataI: c.TEIDDataI = i case ie.TEIDCPlane: c.TEIDCPlane = i case ie.NSAPI: if c.NSAPI == nil { c.NSAPI = i continue } if c.LinkedNSAPI == nil { c.LinkedNSAPI = i continue } c.AdditionalIEs = append(c.AdditionalIEs, i) case ie.ChargingCharacteristics: c.ChargingCharacteristics = i case ie.TraceReference: c.TraceReference = i case ie.TraceType: c.TraceType = i case ie.EndUserAddress: c.EndUserAddress = i case ie.AccessPointName: c.APN = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.GSNAddress: if c.SGSNAddressForSignalling == nil { c.SGSNAddressForSignalling = i continue } if c.SGSNAddressForUserTraffic == nil { c.SGSNAddressForUserTraffic = i continue } c.AdditionalIEs = append(c.AdditionalIEs, i) case ie.MSISDN: c.MSISDN = i case ie.QoSProfile: c.QoSProfile = i case ie.TrafficFlowTemplate: c.TFT = i case ie.TriggerID: c.TriggerID = i case ie.OMCIdentity: c.OMCIdentity = i case ie.CommonFlags: c.CommonFlags = i case ie.APNRestriction: c.APNRestriction = i case ie.RATType: c.RATType = i case ie.UserLocationInformation: c.UserLocationInformation = i case ie.MSTimeZone: c.MSTimeZone = i case ie.IMEISV: c.IMEI = i case ie.CAMELChargingInformationContainer: c.CAMELChargingInformationContainer = i case ie.AdditionalTraceInfo: c.AdditionalTraceInfo = i case ie.CorrelationID: c.CorrelationID = i case ie.EvolvedAllocationRetentionPriorityI: c.EvolvedARPI = i case ie.ExtendedCommonFlags: c.ExtendedCommonFlags = i case ie.UserCSGInformation: c.UCI = i case ie.AggregateMaximumBitRate: c.APNAMBR = i case ie.SignallingPriorityIndication: c.SignallingPriorityIndication = i case ie.CNOperatorSelectionEntity: c.CNOperatorSelectionEntity = i case ie.MappedUEUsageType: c.MappedUEUsageType = i case ie.UPFunctionSelectionIndicationFlags: c.UPFunctionSelectionIndicationFlags = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal returns the byte sequence generated from a CreatePDPContextRequest. func (c *CreatePDPContextRequest) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (c *CreatePDPContextRequest) MarshalTo(b []byte) error { if len(b) < c.MarshalLen() { return ErrTooShortToMarshal } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.IMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RAI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Recovery; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SelectionMode; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TEIDDataI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TEIDCPlane; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.NSAPI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.LinkedNSAPI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChargingCharacteristics; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TraceReference; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TraceType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EndUserAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNAddressForSignalling; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MSISDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.QoSProfile; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TFT; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TriggerID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.OMCIdentity; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CommonFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APNRestriction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RATType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UserLocationInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MSTimeZone; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IMEI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CAMELChargingInformationContainer; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.AdditionalTraceInfo; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CorrelationID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EvolvedARPI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ExtendedCommonFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UCI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APNAMBR; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SignallingPriorityIndication; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CNOperatorSelectionEntity; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MappedUEUsageType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UPFunctionSelectionIndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseCreatePDPContextRequest decodes a given byte sequence as a CreatePDPContextRequest. func ParseCreatePDPContextRequest(b []byte) (*CreatePDPContextRequest, error) { c := &CreatePDPContextRequest{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes a given byte sequence as a CreatePDPContextRequest. func (c *CreatePDPContextRequest) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.RouteingAreaIdentity: c.RAI = i case ie.Recovery: c.Recovery = i case ie.SelectionMode: c.SelectionMode = i case ie.TEIDDataI: c.TEIDDataI = i case ie.TEIDCPlane: c.TEIDCPlane = i case ie.NSAPI: if c.NSAPI == nil { c.NSAPI = i continue } if c.LinkedNSAPI == nil { c.LinkedNSAPI = i continue } c.AdditionalIEs = append(c.AdditionalIEs, i) case ie.ChargingCharacteristics: c.ChargingCharacteristics = i case ie.TraceReference: c.TraceReference = i case ie.TraceType: c.TraceType = i case ie.EndUserAddress: c.EndUserAddress = i case ie.AccessPointName: c.APN = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.GSNAddress: if c.SGSNAddressForSignalling == nil { c.SGSNAddressForSignalling = i continue } if c.SGSNAddressForUserTraffic == nil { c.SGSNAddressForUserTraffic = i continue } c.AdditionalIEs = append(c.AdditionalIEs, i) case ie.MSISDN: c.MSISDN = i case ie.QoSProfile: c.QoSProfile = i case ie.TrafficFlowTemplate: c.TFT = i case ie.TriggerID: c.TriggerID = i case ie.OMCIdentity: c.OMCIdentity = i case ie.CommonFlags: c.CommonFlags = i case ie.APNRestriction: c.APNRestriction = i case ie.RATType: c.RATType = i case ie.UserLocationInformation: c.UserLocationInformation = i case ie.MSTimeZone: c.MSTimeZone = i case ie.IMEISV: c.IMEI = i case ie.CAMELChargingInformationContainer: c.CAMELChargingInformationContainer = i case ie.AdditionalTraceInfo: c.AdditionalTraceInfo = i case ie.CorrelationID: c.CorrelationID = i case ie.EvolvedAllocationRetentionPriorityI: c.EvolvedARPI = i case ie.ExtendedCommonFlags: c.ExtendedCommonFlags = i case ie.UserCSGInformation: c.UCI = i case ie.AggregateMaximumBitRate: c.APNAMBR = i case ie.SignallingPriorityIndication: c.SignallingPriorityIndication = i case ie.CNOperatorSelectionEntity: c.CNOperatorSelectionEntity = i case ie.MappedUEUsageType: c.MappedUEUsageType = i case ie.UPFunctionSelectionIndicationFlags: c.UPFunctionSelectionIndicationFlags = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (c *CreatePDPContextRequest) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.IMSI; ie != nil { l += ie.MarshalLen() } if ie := c.RAI; ie != nil { l += ie.MarshalLen() } if ie := c.Recovery; ie != nil { l += ie.MarshalLen() } if ie := c.SelectionMode; ie != nil { l += ie.MarshalLen() } if ie := c.TEIDDataI; ie != nil { l += ie.MarshalLen() } if ie := c.TEIDCPlane; ie != nil { l += ie.MarshalLen() } if ie := c.NSAPI; ie != nil { l += ie.MarshalLen() } if ie := c.LinkedNSAPI; ie != nil { l += ie.MarshalLen() } if ie := c.ChargingCharacteristics; ie != nil { l += ie.MarshalLen() } if ie := c.TraceReference; ie != nil { l += ie.MarshalLen() } if ie := c.TraceType; ie != nil { l += ie.MarshalLen() } if ie := c.EndUserAddress; ie != nil { l += ie.MarshalLen() } if ie := c.APN; ie != nil { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } if ie := c.SGSNAddressForSignalling; ie != nil { l += ie.MarshalLen() } if ie := c.SGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := c.MSISDN; ie != nil { l += ie.MarshalLen() } if ie := c.QoSProfile; ie != nil { l += ie.MarshalLen() } if ie := c.TFT; ie != nil { l += ie.MarshalLen() } if ie := c.TriggerID; ie != nil { l += ie.MarshalLen() } if ie := c.OMCIdentity; ie != nil { l += ie.MarshalLen() } if ie := c.CommonFlags; ie != nil { l += ie.MarshalLen() } if ie := c.APNRestriction; ie != nil { l += ie.MarshalLen() } if ie := c.RATType; ie != nil { l += ie.MarshalLen() } if ie := c.UserLocationInformation; ie != nil { l += ie.MarshalLen() } if ie := c.MSTimeZone; ie != nil { l += ie.MarshalLen() } if ie := c.IMEI; ie != nil { l += ie.MarshalLen() } if ie := c.CAMELChargingInformationContainer; ie != nil { l += ie.MarshalLen() } if ie := c.AdditionalTraceInfo; ie != nil { l += ie.MarshalLen() } if ie := c.CorrelationID; ie != nil { l += ie.MarshalLen() } if ie := c.EvolvedARPI; ie != nil { l += ie.MarshalLen() } if ie := c.ExtendedCommonFlags; ie != nil { l += ie.MarshalLen() } if ie := c.UCI; ie != nil { l += ie.MarshalLen() } if ie := c.APNAMBR; ie != nil { l += ie.MarshalLen() } if ie := c.SignallingPriorityIndication; ie != nil { l += ie.MarshalLen() } if ie := c.CNOperatorSelectionEntity; ie != nil { l += ie.MarshalLen() } if ie := c.MappedUEUsageType; ie != nil { l += ie.MarshalLen() } if ie := c.UPFunctionSelectionIndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *CreatePDPContextRequest) SetLength() { c.Length = uint16(c.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (c *CreatePDPContextRequest) MessageTypeName() string { return "Create PDP Context Request" } // TEID returns the TEID in human-readable string. func (c *CreatePDPContextRequest) TEID() uint32 { return c.Header.TEID } ================================================ FILE: gtpv1/message/create-pdp-context-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes CreatePDPContextRequest into bytes. // // Deprecated: use CreatePDPContextRequest.Marshal instead. func (c *CreatePDPContextRequest) Serialize() ([]byte, error) { log.Println("CreatePDPContextRequest.Serialize is deprecated. use CreatePDPContextRequest.Marshal instead") return c.Marshal() } // SerializeTo serializes CreatePDPContextRequest into bytes given as b. // // Deprecated: use CreatePDPContextRequest.MarshalTo instead. func (c *CreatePDPContextRequest) SerializeTo(b []byte) error { log.Println("CreatePDPContextRequest.SerializeTo is deprecated. use CreatePDPContextRequest.MarshalTo instead") return c.MarshalTo(b) } // DecodeCreatePDPContextRequest decodes bytes as CreatePDPContextRequest. // // Deprecated: use ParseCreatePDPContextRequest instead. func DecodeCreatePDPContextRequest(b []byte) (*CreatePDPContextRequest, error) { log.Println("DecodeCreatePDPContextRequest is deprecated. use ParseCreatePDPContextRequest instead") return ParseCreatePDPContextRequest(b) } // DecodeFromBytes decodes bytes as CreatePDPContextRequest. // // Deprecated: use CreatePDPContextRequest.UnmarshalBinary instead. func (c *CreatePDPContextRequest) DecodeFromBytes(b []byte) error { log.Println("CreatePDPContextRequest.DecodeFromBytes is deprecated. use CreatePDPContextRequest.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of CreatePDPContextRequest. // // Deprecated: use CreatePDPContextRequest.MarshalLen instead. func (c *CreatePDPContextRequest) Len() int { log.Println("CreatePDPContextRequest.Len is deprecated. use CreatePDPContextRequest.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv1/message/create-pdp-context-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestCreatePDPContextRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewCreatePDPContextRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIMSI("123450123456789"), ie.NewRouteingAreaIdentity("123", "45", 0x1111, 0x22), ie.NewRecovery(254), ie.NewSelectionMode(gtpv1.SelectionModeMSorNetworkProvidedAPNSubscribedVerified), ie.NewTEIDDataI(0xdeadbeef), ie.NewTEIDCPlane(0xdeadbeef), ie.NewNSAPI(5), ie.NewEndUserAddressIPv4(""), ie.NewAccessPointName("some.apn.example"), ie.NewProtocolConfigurationOptions( 0, ie.NewConfigurationProtocolOption(1, []byte{0xde, 0xad, 0xbe, 0xef}), ), ie.NewGSNAddress("1.1.1.1"), ie.NewGSNAddress("2.2.2.2"), ie.NewMSISDN("123412345678"), ie.NewQoSProfile([]byte{0xde, 0xad, 0xbe, 0xef}), // XXX - Implement! ie.NewCommonFlags(0, 0, 1, 0, 0, 0, 0, 0), ie.NewRATType(gtpv1.RatTypeUTRAN), ie.NewUserLocationInformationWithSAI("123", "45", 0x1111, 0x2222), ie.NewMSTimeZone(0x00, 0x00), ), Serialized: []byte{ // Header 0x32, 0x10, 0x00, 0x7f, 0x11, 0x22, 0x33, 0x44, 0x00, 0x01, 0x00, 0x00, // IMSI 0x02, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9, // RAI 0x03, 0x21, 0xf3, 0x54, 0x11, 0x11, 0x22, // Recovery 0x0e, 0xfe, // Selection Mode 0x0f, 0xf0, // TEID-U 0x10, 0xde, 0xad, 0xbe, 0xef, // TEID-C 0x11, 0xde, 0xad, 0xbe, 0xef, // NSAPI 0x14, 0x05, // End User Address 0x80, 0x00, 0x02, 0xf1, 0x21, // APN 0x83, 0x00, 0x11, 0x04, 0x73, 0x6f, 0x6d, 0x65, 0x03, 0x61, 0x70, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, // PCO 0x84, 0x00, 0x08, 0x80, 0x00, 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, // GSN Address 0x85, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, // GSN Address 0x85, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, // MSISDN 0x86, 0x00, 0x07, 0x91, 0x21, 0x43, 0x21, 0x43, 0x65, 0x87, // QoS 0x87, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef, /* XXX - implement QoSProfile! 0x87, 0x00, 0x0f, 0x02, 0x0b, 0x92, 0x1f, 0x73, 0x96, 0xff, 0xff, 0x94, 0xf9, 0xff, 0xff, 0x00, 0x6a, 0x00, */ // Common Flags 0x94, 0x00, 0x01, 0x20, // RAT Type 0x97, 0x00, 0x01, 0x01, // ULI 0x98, 0x00, 0x08, 0x01, 0x21, 0xf3, 0x54, 0x11, 0x11, 0x22, 0x22, // MS Time Zone 0x99, 0x00, 0x02, 0x00, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseCreatePDPContextRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/create-pdp-context-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // CreatePDPContextResponse is a CreatePDPContextResponse Header and its IEs above. type CreatePDPContextResponse struct { *Header Cause *ie.IE ReorderingRequired *ie.IE Recovery *ie.IE TEIDDataI *ie.IE TEIDCPlane *ie.IE NSAPI *ie.IE ChargingID *ie.IE EndUserAddress *ie.IE PCO *ie.IE GGSNAddressForCPlane *ie.IE GGSNAddressForUserTraffic *ie.IE AltGGSNAddressForCPlane *ie.IE AltGGSNAddressForUserTraffic *ie.IE QoSProfile *ie.IE ChargingGatewayAddress *ie.IE AltChargingGatewayAddress *ie.IE CommonFlags *ie.IE APNRestriction *ie.IE MSInfoChangeReportingAction *ie.IE BearerControlMode *ie.IE EvolvedARPI *ie.IE ExtendedCommonFlags *ie.IE CSGInformationReportingAction *ie.IE APNAMBR *ie.IE GGSNBackOffTime *ie.IE ExtendedCommonFlagsII *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewCreatePDPContextResponse creates a new GTPv1 CreatePDPContextResponse. func NewCreatePDPContextResponse(teid uint32, seq uint16, ies ...*ie.IE) *CreatePDPContextResponse { c := &CreatePDPContextResponse{ Header: NewHeader(0x32, MsgTypeCreatePDPContextResponse, teid, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.ReorderingRequired: c.ReorderingRequired = i case ie.Recovery: c.Recovery = i case ie.TEIDDataI: c.TEIDDataI = i case ie.TEIDCPlane: c.TEIDCPlane = i case ie.NSAPI: c.NSAPI = i case ie.ChargingID: c.ChargingID = i case ie.EndUserAddress: c.EndUserAddress = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.GSNAddress: if c.GGSNAddressForCPlane == nil { c.GGSNAddressForCPlane = i continue } if c.GGSNAddressForUserTraffic == nil { c.GGSNAddressForUserTraffic = i continue } if c.AltGGSNAddressForCPlane == nil { c.AltGGSNAddressForCPlane = i continue } c.AltGGSNAddressForUserTraffic = i case ie.QoSProfile: c.QoSProfile = i case ie.ChargingGatewayAddress: if c.ChargingGatewayAddress == nil { c.ChargingGatewayAddress = i } else if c.AltChargingGatewayAddress == nil { c.AltChargingGatewayAddress = i } case ie.CommonFlags: c.CommonFlags = i case ie.APNRestriction: c.APNRestriction = i case ie.MSInfoChangeReportingAction: c.MSInfoChangeReportingAction = i case ie.BearerControlMode: c.BearerControlMode = i case ie.EvolvedAllocationRetentionPriorityI: c.EvolvedARPI = i case ie.ExtendedCommonFlags: c.ExtendedCommonFlags = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.AggregateMaximumBitRate: c.APNAMBR = i case ie.GGSNBackOffTime: c.GGSNBackOffTime = i case ie.ExtendedCommonFlagsII: c.ExtendedCommonFlagsII = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal returns the byte sequence generated from a CreatePDPContextResponse. func (c *CreatePDPContextResponse) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (c *CreatePDPContextResponse) MarshalTo(b []byte) error { if len(b) < c.MarshalLen() { return ErrTooShortToMarshal } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ReorderingRequired; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Recovery; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TEIDDataI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TEIDCPlane; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.NSAPI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChargingID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EndUserAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.GGSNAddressForCPlane; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.GGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.AltGGSNAddressForCPlane; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.AltGGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.QoSProfile; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChargingGatewayAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.AltChargingGatewayAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CommonFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APNRestriction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MSInfoChangeReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.BearerControlMode; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EvolvedARPI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ExtendedCommonFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APNAMBR; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.GGSNBackOffTime; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ExtendedCommonFlagsII; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseCreatePDPContextResponse decodes a given byte sequence as a CreatePDPContextResponse. func ParseCreatePDPContextResponse(b []byte) (*CreatePDPContextResponse, error) { c := &CreatePDPContextResponse{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes a given byte sequence as a CreatePDPContextResponse. func (c *CreatePDPContextResponse) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.ReorderingRequired: c.ReorderingRequired = i case ie.Recovery: c.Recovery = i case ie.TEIDDataI: c.TEIDDataI = i case ie.TEIDCPlane: c.TEIDCPlane = i case ie.NSAPI: c.NSAPI = i case ie.ChargingID: c.ChargingID = i case ie.EndUserAddress: c.EndUserAddress = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.GSNAddress: if c.GGSNAddressForCPlane == nil { c.GGSNAddressForCPlane = i continue } if c.GGSNAddressForUserTraffic == nil { c.GGSNAddressForUserTraffic = i continue } if c.AltGGSNAddressForCPlane == nil { c.AltGGSNAddressForCPlane = i continue } c.AltGGSNAddressForUserTraffic = i case ie.QoSProfile: c.QoSProfile = i case ie.ChargingGatewayAddress: if c.ChargingGatewayAddress == nil { c.ChargingGatewayAddress = i continue } c.AltChargingGatewayAddress = i case ie.CommonFlags: c.CommonFlags = i case ie.APNRestriction: c.APNRestriction = i case ie.MSInfoChangeReportingAction: c.MSInfoChangeReportingAction = i case ie.BearerControlMode: c.BearerControlMode = i case ie.EvolvedAllocationRetentionPriorityI: c.EvolvedARPI = i case ie.ExtendedCommonFlags: c.ExtendedCommonFlags = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.AggregateMaximumBitRate: c.APNAMBR = i case ie.GGSNBackOffTime: c.GGSNBackOffTime = i case ie.ExtendedCommonFlagsII: c.ExtendedCommonFlagsII = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (c *CreatePDPContextResponse) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.Cause; ie != nil { l += ie.MarshalLen() } if ie := c.ReorderingRequired; ie != nil { l += ie.MarshalLen() } if ie := c.Recovery; ie != nil { l += ie.MarshalLen() } if ie := c.TEIDDataI; ie != nil { l += ie.MarshalLen() } if ie := c.TEIDCPlane; ie != nil { l += ie.MarshalLen() } if ie := c.NSAPI; ie != nil { l += ie.MarshalLen() } if ie := c.ChargingID; ie != nil { l += ie.MarshalLen() } if ie := c.EndUserAddress; ie != nil { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } if ie := c.GGSNAddressForCPlane; ie != nil { l += ie.MarshalLen() } if ie := c.GGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := c.AltGGSNAddressForCPlane; ie != nil { l += ie.MarshalLen() } if ie := c.AltGGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := c.QoSProfile; ie != nil { l += ie.MarshalLen() } if ie := c.ChargingGatewayAddress; ie != nil { l += ie.MarshalLen() } if ie := c.AltChargingGatewayAddress; ie != nil { l += ie.MarshalLen() } if ie := c.CommonFlags; ie != nil { l += ie.MarshalLen() } if ie := c.APNRestriction; ie != nil { l += ie.MarshalLen() } if ie := c.MSInfoChangeReportingAction; ie != nil { l += ie.MarshalLen() } if ie := c.BearerControlMode; ie != nil { l += ie.MarshalLen() } if ie := c.EvolvedARPI; ie != nil { l += ie.MarshalLen() } if ie := c.ExtendedCommonFlags; ie != nil { l += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { l += ie.MarshalLen() } if ie := c.APNAMBR; ie != nil { l += ie.MarshalLen() } if ie := c.GGSNBackOffTime; ie != nil { l += ie.MarshalLen() } if ie := c.ExtendedCommonFlagsII; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *CreatePDPContextResponse) SetLength() { c.Length = uint16(c.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (c *CreatePDPContextResponse) MessageTypeName() string { return "Create PDP Context Response" } // TEID returns the TEID in human-readable string. func (c *CreatePDPContextResponse) TEID() uint32 { return c.Header.TEID } ================================================ FILE: gtpv1/message/create-pdp-context-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes CreatePDPContextResponse into bytes. // // Deprecated: use CreatePDPContextResponse.Marshal instead. func (c *CreatePDPContextResponse) Serialize() ([]byte, error) { log.Println("CreatePDPContextResponse.Serialize is deprecated. use CreatePDPContextResponse.Marshal instead") return c.Marshal() } // SerializeTo serializes CreatePDPContextResponse into bytes given as b. // // Deprecated: use CreatePDPContextResponse.MarshalTo instead. func (c *CreatePDPContextResponse) SerializeTo(b []byte) error { log.Println("CreatePDPContextResponse.SerializeTo is deprecated. use CreatePDPContextResponse.MarshalTo instead") return c.MarshalTo(b) } // DecodeCreatePDPContextResponse decodes bytes as CreatePDPContextResponse. // // Deprecated: use ParseCreatePDPContextResponse instead. func DecodeCreatePDPContextResponse(b []byte) (*CreatePDPContextResponse, error) { log.Println("DecodeCreatePDPContextResponse is deprecated. use ParseCreatePDPContextResponse instead") return ParseCreatePDPContextResponse(b) } // DecodeFromBytes decodes bytes as CreatePDPContextResponse. // // Deprecated: use CreatePDPContextResponse.UnmarshalBinary instead. func (c *CreatePDPContextResponse) DecodeFromBytes(b []byte) error { log.Println("CreatePDPContextResponse.DecodeFromBytes is deprecated. use CreatePDPContextResponse.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of CreatePDPContextResponse. // // Deprecated: use CreatePDPContextResponse.MarshalLen instead. func (c *CreatePDPContextResponse) Len() int { log.Println("CreatePDPContextResponse.Len is deprecated. use CreatePDPContextResponse.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv1/message/create-pdp-context-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestCreatePDPContextResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewCreatePDPContextResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv1.ResCauseRequestAccepted), ie.NewReorderingRequired(false), ie.NewRecovery(0), ie.NewTEIDDataI(0xdeadbeef), ie.NewTEIDCPlane(0xdeadbeef), ie.NewChargingID(1), ie.NewEndUserAddress("10.10.10.10"), ie.NewGSNAddress("1.1.1.1"), ie.NewGSNAddress("2.2.2.2"), ), Serialized: []byte{ // Header 0x32, 0x11, 0x00, 0x30, 0x11, 0x22, 0x33, 0x44, 0x00, 0x01, 0x00, 0x00, // Cause 0x01, 0x80, // ReorderingRequired 0x08, 0xfe, // Recovery 0x0e, 0x00, // TEID-U 0x10, 0xde, 0xad, 0xbe, 0xef, // TEID-C 0x11, 0xde, 0xad, 0xbe, 0xef, // ChargingID 0x7f, 0x00, 0x00, 0x00, 0x01, // End User Address 0x80, 0x00, 0x06, 0xf1, 0x21, 0x0a, 0x0a, 0x0a, 0x0a, // GSN Address 0x85, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, // GSN Address 0x85, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseCreatePDPContextResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/delete-pdp-context-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // DeletePDPContextRequest is a DeletePDPContextRequest Header and its IEs above. type DeletePDPContextRequest struct { *Header Cause *ie.IE TeardownInd *ie.IE NSAPI *ie.IE PCO *ie.IE ULI *ie.IE MSTimeZone *ie.IE ExtendedCommonFlags *ie.IE ULITimestamp *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeletePDPContextRequest creates a new GTPv1 DeletePDPContextRequest. func NewDeletePDPContextRequest(teid uint32, seq uint16, ies ...*ie.IE) *DeletePDPContextRequest { d := &DeletePDPContextRequest{ Header: NewHeader(0x32, MsgTypeDeletePDPContextRequest, teid, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.TeardownInd: d.TeardownInd = i case ie.NSAPI: d.NSAPI = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.UserLocationInformation: d.ULI = i case ie.MSTimeZone: d.MSTimeZone = i case ie.ExtendedCommonFlags: d.ExtendedCommonFlags = i case ie.ULITimestamp: d.ULITimestamp = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal returns the byte sequence generated from a DeletePDPContextRequest. func (d *DeletePDPContextRequest) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (d *DeletePDPContextRequest) MarshalTo(b []byte) error { if len(b) < d.MarshalLen() { return ErrTooShortToMarshal } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.TeardownInd; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.NSAPI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PCO; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.MSTimeZone; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ExtendedCommonFlags; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeletePDPContextRequest decodes a given byte sequence as a DeletePDPContextRequest. func ParseDeletePDPContextRequest(b []byte) (*DeletePDPContextRequest, error) { d := &DeletePDPContextRequest{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes a given byte sequence as a DeletePDPContextRequest. func (d *DeletePDPContextRequest) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.TeardownInd: d.TeardownInd = i case ie.NSAPI: d.NSAPI = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.UserLocationInformation: d.ULI = i case ie.MSTimeZone: d.MSTimeZone = i case ie.ExtendedCommonFlags: d.ExtendedCommonFlags = i case ie.ULITimestamp: d.ULITimestamp = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (d *DeletePDPContextRequest) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.TeardownInd; ie != nil { l += ie.MarshalLen() } if ie := d.NSAPI; ie != nil { l += ie.MarshalLen() } if ie := d.PCO; ie != nil { l += ie.MarshalLen() } if ie := d.ULI; ie != nil { l += ie.MarshalLen() } if ie := d.MSTimeZone; ie != nil { l += ie.MarshalLen() } if ie := d.ExtendedCommonFlags; ie != nil { l += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeletePDPContextRequest) SetLength() { d.Length = uint16(d.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (d *DeletePDPContextRequest) MessageTypeName() string { return "Delete PDP Context Request" } // TEID returns the TEID in human-readable string. func (d *DeletePDPContextRequest) TEID() uint32 { return d.Header.TEID } ================================================ FILE: gtpv1/message/delete-pdp-context-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes DeletePDPContextRequest into bytes. // // Deprecated: use DeletePDPContextRequest.Marshal instead. func (d *DeletePDPContextRequest) Serialize() ([]byte, error) { log.Println("DeletePDPContextRequest.Serialize is deprecated. use DeletePDPContextRequest.Marshal instead") return d.Marshal() } // SerializeTo serializes DeletePDPContextRequest into bytes given as b. // // Deprecated: use DeletePDPContextRequest.MarshalTo instead. func (d *DeletePDPContextRequest) SerializeTo(b []byte) error { log.Println("DeletePDPContextRequest.SerializeTo is deprecated. use DeletePDPContextRequest.MarshalTo instead") return d.MarshalTo(b) } // DecodeDeletePDPContextRequest decodes bytes as DeletePDPContextRequest. // // Deprecated: use ParseDeletePDPContextRequest instead. func DecodeDeletePDPContextRequest(b []byte) (*DeletePDPContextRequest, error) { log.Println("DecodeDeletePDPContextRequest is deprecated. use ParseDeletePDPContextRequest instead") return ParseDeletePDPContextRequest(b) } // DecodeFromBytes decodes bytes as DeletePDPContextRequest. // // Deprecated: use DeletePDPContextRequest.UnmarshalBinary instead. func (d *DeletePDPContextRequest) DecodeFromBytes(b []byte) error { log.Println("DeletePDPContextRequest.DecodeFromBytes is deprecated. use DeletePDPContextRequest.UnmarshalBinary instead") return d.UnmarshalBinary(b) } // Len returns the actual length of DeletePDPContextRequest. // // Deprecated: use DeletePDPContextRequest.MarshalLen instead. func (d *DeletePDPContextRequest) Len() int { log.Println("DeletePDPContextRequest.Len is deprecated. use DeletePDPContextRequest.MarshalLen instead") return d.MarshalLen() } ================================================ FILE: gtpv1/message/delete-pdp-context-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestDeletePDPContextRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDeletePDPContextRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv1.ReqCauseNetworkFailure), ie.NewNSAPI(5), ), Serialized: []byte{ // Header 0x32, 0x14, 0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x00, 0x01, 0x00, 0x00, // Cause 0x01, 0x08, // NSAPI 0x14, 0x05, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeletePDPContextRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/delete-pdp-context-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // DeletePDPContextResponse is a DeletePDPContextResponse Header and its IEs above. type DeletePDPContextResponse struct { *Header Cause *ie.IE PCO *ie.IE ULI *ie.IE MSTimeZone *ie.IE ULITimestamp *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeletePDPContextResponse creates a new GTPv1 DeletePDPContextResponse. func NewDeletePDPContextResponse(teid uint32, seq uint16, ies ...*ie.IE) *DeletePDPContextResponse { d := &DeletePDPContextResponse{ Header: NewHeader(0x32, MsgTypeDeletePDPContextResponse, teid, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.UserLocationInformation: d.ULI = i case ie.MSTimeZone: d.MSTimeZone = i case ie.ULITimestamp: d.ULITimestamp = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal returns the byte sequence generated from a DeletePDPContextResponse. func (d *DeletePDPContextResponse) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (d *DeletePDPContextResponse) MarshalTo(b []byte) error { if len(b) < d.MarshalLen() { return ErrTooShortToMarshal } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PCO; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.MSTimeZone; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeletePDPContextResponse decodes a given byte sequence as a DeletePDPContextResponse. func ParseDeletePDPContextResponse(b []byte) (*DeletePDPContextResponse, error) { d := &DeletePDPContextResponse{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes a given byte sequence as a DeletePDPContextResponse. func (d *DeletePDPContextResponse) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.UserLocationInformation: d.ULI = i case ie.MSTimeZone: d.MSTimeZone = i case ie.ULITimestamp: d.ULITimestamp = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (d *DeletePDPContextResponse) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.PCO; ie != nil { l += ie.MarshalLen() } if ie := d.ULI; ie != nil { l += ie.MarshalLen() } if ie := d.MSTimeZone; ie != nil { l += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeletePDPContextResponse) SetLength() { d.Length = uint16(d.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (d *DeletePDPContextResponse) MessageTypeName() string { return "Delete PDP Context Response" } // TEID returns the TEID in human-readable string. func (d *DeletePDPContextResponse) TEID() uint32 { return d.Header.TEID } ================================================ FILE: gtpv1/message/delete-pdp-context-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes DeletePDPContextResponse into bytes. // // Deprecated: use DeletePDPContextResponse.Marshal instead. func (d *DeletePDPContextResponse) Serialize() ([]byte, error) { log.Println("DeletePDPContextResponse.Serialize is deprecated. use DeletePDPContextResponse.Marshal instead") return d.Marshal() } // SerializeTo serializes DeletePDPContextResponse into bytes given as b. // // Deprecated: use DeletePDPContextResponse.MarshalTo instead. func (d *DeletePDPContextResponse) SerializeTo(b []byte) error { log.Println("DeletePDPContextResponse.SerializeTo is deprecated. use DeletePDPContextResponse.MarshalTo instead") return d.MarshalTo(b) } // DecodeDeletePDPContextResponse decodes bytes as DeletePDPContextResponse. // // Deprecated: use ParseDeletePDPContextResponse instead. func DecodeDeletePDPContextResponse(b []byte) (*DeletePDPContextResponse, error) { log.Println("DecodeDeletePDPContextResponse is deprecated. use ParseDeletePDPContextResponse instead") return ParseDeletePDPContextResponse(b) } // DecodeFromBytes decodes bytes as DeletePDPContextResponse. // // Deprecated: use DeletePDPContextResponse.UnmarshalBinary instead. func (d *DeletePDPContextResponse) DecodeFromBytes(b []byte) error { log.Println("DeletePDPContextResponse.DecodeFromBytes is deprecated. use DeletePDPContextResponse.UnmarshalBinary instead") return d.UnmarshalBinary(b) } // Len returns the actual length of DeletePDPContextResponse. // // Deprecated: use DeletePDPContextResponse.MarshalLen instead. func (d *DeletePDPContextResponse) Len() int { log.Println("DeletePDPContextResponse.Len is deprecated. use DeletePDPContextResponse.MarshalLen instead") return d.MarshalLen() } ================================================ FILE: gtpv1/message/delete-pdp-context-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestDeletePDPContextResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDeletePDPContextResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv1.ResCauseRequestAccepted), ), Serialized: []byte{ // Header 0x32, 0x15, 0x00, 0x06, 0x11, 0x22, 0x33, 0x44, 0x00, 0x01, 0x00, 0x00, // Cause 0x01, 0x80, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeletePDPContextResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/echo-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // EchoRequest is a EchoRequest Header and its IEs above. type EchoRequest struct { *Header PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewEchoRequest creates a new GTP. func NewEchoRequest(seq uint16, ies ...*ie.IE) *EchoRequest { e := &EchoRequest{ Header: NewHeader(0x32, MsgTypeEchoRequest, 0, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } e.SetLength() return e } // Marshal returns the byte sequence generated from a EchoRequest. func (e *EchoRequest) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *EchoRequest) MarshalTo(b []byte) error { if e.Header.Payload != nil { e.Header.Payload = nil } e.Header.Payload = make([]byte, e.MarshalLen()-e.Header.MarshalLen()) offset := 0 if ie := e.PrivateExtension; ie != nil { if err := ie.MarshalTo(e.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } e.Header.SetLength() return e.Header.MarshalTo(b) } // ParseEchoRequest decodes a given byte sequence as a EchoRequest. func ParseEchoRequest(b []byte) (*EchoRequest, error) { e := &EchoRequest{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary decodes a given byte sequence as a EchoRequest. func (e *EchoRequest) UnmarshalBinary(b []byte) error { var err error e.Header, err = ParseHeader(b) if err != nil { return err } ies, err := ie.ParseMultiIEs(e.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (e *EchoRequest) MarshalLen() int { l := e.Header.MarshalLen() - len(e.Header.Payload) if ie := e.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (e *EchoRequest) SetLength() { e.Header.Length = uint16(e.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (e *EchoRequest) MessageTypeName() string { return "Echo Request" } // TEID returns the TEID in human-readable string. func (e *EchoRequest) TEID() uint32 { return e.Header.TEID } ================================================ FILE: gtpv1/message/echo-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes EchoRequest into bytes. // // Deprecated: use EchoRequest.Marshal instead. func (e *EchoRequest) Serialize() ([]byte, error) { log.Println("EchoRequest.Serialize is deprecated. use EchoRequest.Marshal instead") return e.Marshal() } // SerializeTo serializes EchoRequest into bytes given as b. // // Deprecated: use EchoRequest.MarshalTo instead. func (e *EchoRequest) SerializeTo(b []byte) error { log.Println("EchoRequest.SerializeTo is deprecated. use EchoRequest.MarshalTo instead") return e.MarshalTo(b) } // DecodeEchoRequest decodes bytes as EchoRequest. // // Deprecated: use ParseEchoRequest instead. func DecodeEchoRequest(b []byte) (*EchoRequest, error) { log.Println("DecodeEchoRequest is deprecated. use ParseEchoRequest instead") return ParseEchoRequest(b) } // DecodeFromBytes decodes bytes as EchoRequest. // // Deprecated: use EchoRequest.UnmarshalBinary instead. func (e *EchoRequest) DecodeFromBytes(b []byte) error { log.Println("EchoRequest.DecodeFromBytes is deprecated. use EchoRequest.UnmarshalBinary instead") return e.UnmarshalBinary(b) } // Len returns the actual length of EchoRequest. // // Deprecated: use EchoRequest.MarshalLen instead. func (e *EchoRequest) Len() int { log.Println("EchoRequest.Len is deprecated. use EchoRequest.MarshalLen instead") return e.MarshalLen() } ================================================ FILE: gtpv1/message/echo-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestEchoRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewEchoRequest(0), Serialized: []byte{ 0x32, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseEchoRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/echo-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // EchoResponse is a EchoResponse Header and its IEs above. type EchoResponse struct { *Header Recovery *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewEchoResponse creates a new GTPv1 EchoResponse. func NewEchoResponse(seq uint16, ies ...*ie.IE) *EchoResponse { e := &EchoResponse{ Header: NewHeader(0x32, MsgTypeEchoResponse, 0, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Recovery: e.Recovery = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } e.SetLength() return e } // Marshal returns the byte sequence generated from a EchoResponse. func (e *EchoResponse) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *EchoResponse) MarshalTo(b []byte) error { if e.Header.Payload != nil { e.Header.Payload = nil } e.Header.Payload = make([]byte, e.MarshalLen()-e.Header.MarshalLen()) offset := 0 if ie := e.Recovery; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } e.Header.SetLength() return e.Header.MarshalTo(b) } // ParseEchoResponse decodes a given byte sequence as a EchoResponse. func ParseEchoResponse(b []byte) (*EchoResponse, error) { e := &EchoResponse{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary decodes a given byte sequence as a EchoResponse. func (e *EchoResponse) UnmarshalBinary(b []byte) error { var err error e.Header, err = ParseHeader(b) if err != nil { return err } if len(e.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(e.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Recovery: e.Recovery = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (e *EchoResponse) MarshalLen() int { l := e.Header.MarshalLen() - len(e.Header.Payload) if ie := e.Recovery; ie != nil { l += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (e *EchoResponse) SetLength() { e.Header.Length = uint16(e.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (e *EchoResponse) MessageTypeName() string { return "Echo Response" } // TEID returns the TEID in human-readable string. func (e *EchoResponse) TEID() uint32 { return e.Header.TEID } ================================================ FILE: gtpv1/message/echo-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes EchoResponse into bytes. // // Deprecated: use EchoResponse.Marshal instead. func (e *EchoResponse) Serialize() ([]byte, error) { log.Println("EchoResponse.Serialize is deprecated. use EchoResponse.Marshal instead") return e.Marshal() } // SerializeTo serializes EchoResponse into bytes given as b. // // Deprecated: use EchoResponse.MarshalTo instead. func (e *EchoResponse) SerializeTo(b []byte) error { log.Println("EchoResponse.SerializeTo is deprecated. use EchoResponse.MarshalTo instead") return e.MarshalTo(b) } // DecodeEchoResponse decodes bytes as EchoResponse. // // Deprecated: use ParseEchoResponse instead. func DecodeEchoResponse(b []byte) (*EchoResponse, error) { log.Println("DecodeEchoResponse is deprecated. use ParseEchoResponse instead") return ParseEchoResponse(b) } // DecodeFromBytes decodes bytes as EchoResponse. // // Deprecated: use EchoResponse.UnmarshalBinary instead. func (e *EchoResponse) DecodeFromBytes(b []byte) error { log.Println("EchoResponse.DecodeFromBytes is deprecated. use EchoResponse.UnmarshalBinary instead") return e.UnmarshalBinary(b) } // Len returns the actual length of EchoResponse. // // Deprecated: use EchoResponse.MarshalLen instead. func (e *EchoResponse) Len() int { log.Println("EchoResponse.Len is deprecated. use EchoResponse.MarshalLen instead") return e.MarshalLen() } ================================================ FILE: gtpv1/message/echo-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestEchoResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewEchoResponse(0, ie.NewRecovery(0x80)), Serialized: []byte{ 0x32, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseEchoResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/end-marker.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // EndMarker is a EndMarker Header and its IEs above. type EndMarker struct { *Header PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewEndMarker creates a new GTP. func NewEndMarker(ies ...*ie.IE) *EndMarker { e := &EndMarker{ Header: NewHeader(0x30, MsgTypeEndMarker, 0, 0, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } e.SetLength() return e } // Marshal returns the byte sequence generated from a EndMarker. func (e *EndMarker) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *EndMarker) MarshalTo(b []byte) error { if e.Header.Payload != nil { e.Header.Payload = nil } e.Header.Payload = make([]byte, e.MarshalLen()-e.Header.MarshalLen()) offset := 0 if ie := e.PrivateExtension; ie != nil { if err := ie.MarshalTo(e.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } e.Header.SetLength() return e.Header.MarshalTo(b) } // ParseEndMarker decodes a given byte sequence as a EndMarker. func ParseEndMarker(b []byte) (*EndMarker, error) { e := &EndMarker{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary decodes a given byte sequence as a EndMarker. func (e *EndMarker) UnmarshalBinary(b []byte) error { var err error e.Header, err = ParseHeader(b) if err != nil { return err } ies, err := ie.ParseMultiIEs(e.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (e *EndMarker) MarshalLen() int { l := e.Header.MarshalLen() - len(e.Header.Payload) if ie := e.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (e *EndMarker) SetLength() { e.Header.Length = uint16(e.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (e *EndMarker) MessageTypeName() string { return "End Marker" } // TEID returns the TEID in human-readable string. func (e *EndMarker) TEID() uint32 { return e.Header.TEID } ================================================ FILE: gtpv1/message/end-marker_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestEndMarker(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewEndMarker(), Serialized: []byte{ 0x30, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseEndMarker(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/error-indication.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv1/ie" // ErrorIndication is a ErrorIndication Header and its IEs above. type ErrorIndication struct { *Header TEIDDataI *ie.IE GTPUPeerAddress *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewErrorIndication creates a new GTPv1 ErrorIndication. func NewErrorIndication(teid uint32, seq uint16, ies ...*ie.IE) *ErrorIndication { e := &ErrorIndication{ Header: NewHeader(0x32, MsgTypeErrorIndication, teid, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.TEIDDataI: e.TEIDDataI = i case ie.GSNAddress: e.GTPUPeerAddress = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } e.SetLength() return e } // Marshal returns the byte sequence generated from a ErrorIndication. func (e *ErrorIndication) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *ErrorIndication) MarshalTo(b []byte) error { if len(b) < e.MarshalLen() { return ErrTooShortToMarshal } e.Header.Payload = make([]byte, e.MarshalLen()-e.Header.MarshalLen()) offset := 0 if ie := e.TEIDDataI; ie != nil { if err := ie.MarshalTo(e.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := e.GTPUPeerAddress; ie != nil { if err := ie.MarshalTo(e.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { if err := ie.MarshalTo(e.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } e.Header.SetLength() return e.Header.MarshalTo(b) } // ParseErrorIndication decodes a given byte sequence as a ErrorIndication. func ParseErrorIndication(b []byte) (*ErrorIndication, error) { e := &ErrorIndication{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary decodes a given byte sequence as a ErrorIndication. func (e *ErrorIndication) UnmarshalBinary(b []byte) error { var err error e.Header, err = ParseHeader(b) if err != nil { return err } if len(e.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(e.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.TEIDDataI: e.TEIDDataI = i case ie.GSNAddress: e.GTPUPeerAddress = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (e *ErrorIndication) MarshalLen() int { l := e.Header.MarshalLen() - len(e.Header.Payload) if ie := e.TEIDDataI; ie != nil { l += ie.MarshalLen() } if ie := e.GTPUPeerAddress; ie != nil { l += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (e *ErrorIndication) SetLength() { e.Length = uint16(e.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (e *ErrorIndication) MessageTypeName() string { return "Errror Indication" } // TEID returns the TEID in human-readable string. func (e *ErrorIndication) TEID() uint32 { return e.Header.TEID } ================================================ FILE: gtpv1/message/error-indication_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ErrorIndication into bytes. // // Deprecated: use ErrorIndication.Marshal instead. func (e *ErrorIndication) Serialize() ([]byte, error) { log.Println("ErrorIndication.Serialize is deprecated. use ErrorIndication.Marshal instead") return e.Marshal() } // SerializeTo serializes ErrorIndication into bytes given as b. // // Deprecated: use ErrorIndication.MarshalTo instead. func (e *ErrorIndication) SerializeTo(b []byte) error { log.Println("ErrorIndication.SerializeTo is deprecated. use ErrorIndication.MarshalTo instead") return e.MarshalTo(b) } // DecodeErrorIndication decodes bytes as ErrorIndication. // // Deprecated: use ParseErrorIndication instead. func DecodeErrorIndication(b []byte) (*ErrorIndication, error) { log.Println("DecodeErrorIndication is deprecated. use ParseErrorIndication instead") return ParseErrorIndication(b) } // DecodeFromBytes decodes bytes as ErrorIndication. // // Deprecated: use ErrorIndication.UnmarshalBinary instead. func (e *ErrorIndication) DecodeFromBytes(b []byte) error { log.Println("ErrorIndication.DecodeFromBytes is deprecated. use ErrorIndication.UnmarshalBinary instead") return e.UnmarshalBinary(b) } // Len returns the actual length of ErrorIndication. // // Deprecated: use ErrorIndication.MarshalLen instead. func (e *ErrorIndication) Len() int { log.Println("ErrorIndication.Len is deprecated. use ErrorIndication.MarshalLen instead") return e.MarshalLen() } ================================================ FILE: gtpv1/message/error-indication_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestErrorIndication(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewErrorIndication( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewTEIDDataI(0xdeadbeef), ie.NewGSNAddress("1.1.1.1"), ), Serialized: []byte{ // Header 0x32, 0x1a, 0x00, 0x10, 0x11, 0x22, 0x33, 0x44, 0x00, 0x01, 0x00, 0x00, // TEID-U 0x10, 0xde, 0xad, 0xbe, 0xef, // GSN Address 0x85, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseErrorIndication(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/errors.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "errors" "fmt" ) // Error definitions. var ( ErrInvalidLength = errors.New("got invalid length ") ErrTooShortToMarshal = errors.New("too short to serialize") ErrTooShortToParse = errors.New("too short to decode as GTPv1") ErrInvalidMessageType = errors.New("got invalid message type") ) // InvalidTypeError indicates the type of an ExtensionHeader is invalid. type InvalidTypeError struct { Type uint8 } // Error returns message with the invalid type given. func (e *InvalidTypeError) Error() string { return fmt.Sprintf("got invalid type: %v", e.Type) } ================================================ FILE: gtpv1/message/extension-header.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "fmt" // ExtensionHeaderType definitions. const ( ExtHeaderTypeNoMoreExtensionHeaders uint8 = 0b00000000 ExtHeaderTypeMBMSSupportIndication uint8 = 0b00000001 ExtHeaderTypeMSInfoChangeReportingSupportIndication uint8 = 0b00000010 ExtHeaderTypeLongPDCPPDUNumber uint8 = 0b00000011 ExtHeaderTypeServiceClassIndicator uint8 = 0b00100000 ExtHeaderTypeUDPPort uint8 = 0b01000000 ExtHeaderTypeRANContainer uint8 = 0b10000001 ExtHeaderTypeLongPDCPPDUNumberRequired uint8 = 0b10000010 ExtHeaderTypeXwRANContainer uint8 = 0b10000011 ExtHeaderTypeNRRANContainer uint8 = 0b10000100 ExtHeaderTypePDUSessionContainer uint8 = 0b10000101 ExtHeaderTypePDCPPDUNumber uint8 = 0b11000000 ExtHeaderTypeSuspendRequest uint8 = 0b11000001 ExtHeaderTypeSuspendResponse uint8 = 0b11000010 ) // ExtensionHeader represents an Extension Header defined in §5.2, TS 29.281 and §6.1 TS 29.060. type ExtensionHeader struct { Type uint8 // this doesn't exist in the spec, but it's apparently helpful to have Length uint8 Content []byte NextType uint8 } // NewExtensionHeader creates a new ExtensionHeader. // // ExtensionHeader struct has its own type while it does not actually exist in the packet. // Be sure to set an appropriate one - putting a wrong type may cause unexpected errors while // using method depends on ExtensionHeader struct. func NewExtensionHeader(typ uint8, content []byte, nextType uint8) *ExtensionHeader { eh := &ExtensionHeader{ Type: typ, Content: content, NextType: nextType, } eh.SetLength() return eh } // Marshal returns the byte sequence generated from an ExtensionHeader. func (e *ExtensionHeader) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *ExtensionHeader) MarshalTo(b []byte) error { l := len(b) if l < e.MarshalLen() { return ErrTooShortToMarshal } b[0] = e.Length offset := int(e.Length)*4 - 1 if l < offset+1 { return ErrTooShortToMarshal } copy(b[1:offset], e.Content) b[offset] = e.NextType return nil } // ParseExtensionHeader decodes given byte sequence as a ExtensionHeader. func ParseExtensionHeader(b []byte) (*ExtensionHeader, error) { e := &ExtensionHeader{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary sets the values retrieved from byte sequence in ExtensionHeader. func (e *ExtensionHeader) UnmarshalBinary(b []byte) error { l := len(b) if l < 2 { return ErrTooShortToParse } e.Length = b[0] if e.Length == 0 { return ErrInvalidLength } offset := int(e.Length)*4 - 1 if l < offset+1 { return ErrTooShortToParse } e.Content = b[1:offset] e.NextType = b[offset] return nil } // ParseMultiExtensionHeaders parses given bytes sequence as multiple ExtensionHeaders. func ParseMultiExtensionHeaders(b []byte) ([]*ExtensionHeader, error) { var ehs []*ExtensionHeader next := ExtHeaderTypeNoMoreExtensionHeaders for { eh, err := ParseExtensionHeader(b) if err != nil { return nil, err } eh.Type = next ehs = append(ehs, eh) b = b[eh.MarshalLen():] next = eh.NextType if next == ExtHeaderTypeNoMoreExtensionHeaders { break } continue } return ehs, nil } // MarshalLen returns the serial length of ExtensionHeader. func (e *ExtensionHeader) MarshalLen() int { return pad4Len(len(e.Content) + 2) } // SetLength sets the length calculated from the length of contents to Length field. func (e *ExtensionHeader) SetLength() { e.Length = uint8(pad4Len(len(e.Content)+2) / 4) } // String returns an ExtensionHeader fields in human readable format. func (e *ExtensionHeader) String() string { return fmt.Sprintf("{Type: %#x, Length: %d, Content: %#x, NextType: %x}", e.Type, e.Length, e.Content, e.NextType, ) } // IsComprehensionRequired reports whether the comprehension of the ExtensionHeader is // required or not. func (e *ExtensionHeader) IsComprehensionRequired() bool { return e.Type>>7 == 1 } func pad4Len(n int) int { return n + ((4 - n) & 0b11) } ================================================ FILE: gtpv1/message/generic.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "fmt" "github.com/wmnsk/go-gtp/gtpv1/ie" ) // Generic is a Generic Header and its IEs above. // This is for handling a non-implemented type of message. type Generic struct { *Header IEs []*ie.IE } // NewGeneric creates a new GTPv1 Generic. func NewGeneric(msgType uint8, teid uint32, seq uint16, ie ...*ie.IE) *Generic { g := &Generic{ Header: NewHeader(0x32, msgType, teid, seq, nil), IEs: ie, } g.SetLength() return g } // Marshal returns the byte sequence generated from a Generic. func (g *Generic) Marshal() ([]byte, error) { b := make([]byte, g.MarshalLen()) if err := g.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (g *Generic) MarshalTo(b []byte) error { if g.Header.Payload != nil { g.Header.Payload = nil } g.Header.Payload = make([]byte, g.MarshalLen()-g.Header.MarshalLen()) offset := 0 for _, ie := range g.IEs { if ie == nil { continue } if err := ie.MarshalTo(g.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } g.Header.SetLength() return g.Header.MarshalTo(b) } // ParseGeneric decodes a given byte sequence as a Generic. func ParseGeneric(b []byte) (*Generic, error) { g := &Generic{} if err := g.UnmarshalBinary(b); err != nil { return nil, err } return g, nil } // UnmarshalBinary decodes a given byte sequence as a Generic. func (g *Generic) UnmarshalBinary(b []byte) error { var err error g.Header, err = ParseHeader(b) if err != nil { return err } if len(g.Header.Payload) < 2 { return nil } g.IEs, err = ie.ParseMultiIEs(g.Header.Payload) if err != nil { return err } return nil } // MarshalLen returns the serial length of Data. func (g *Generic) MarshalLen() int { l := g.Header.MarshalLen() - len(g.Header.Payload) for _, ie := range g.IEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (g *Generic) SetLength() { g.Header.Length = uint16(g.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (g *Generic) MessageTypeName() string { return fmt.Sprintf("Unknown (%d)", g.Type) } // TEID returns the TEID in human-readable string. func (g *Generic) TEID() uint32 { return g.Header.TEID } // AddIE add IEs to Generic type of GTPv2 message and update Length field. func (g *Generic) AddIE(ie ...*ie.IE) { g.IEs = append(g.IEs, ie...) g.SetLength() } ================================================ FILE: gtpv1/message/generic_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes Generic into bytes. // // Deprecated: use Generic.Marshal instead. func (g *Generic) Serialize() ([]byte, error) { log.Println("Generic.Serialize is deprecated. use Generic.Marshal instead") return g.Marshal() } // SerializeTo serializes Generic into bytes given as b. // // Deprecated: use Generic.MarshalTo instead. func (g *Generic) SerializeTo(b []byte) error { log.Println("Generic.SerializeTo is deprecated. use Generic.MarshalTo instead") return g.MarshalTo(b) } // DecodeGeneric decodes bytes as Generic. // // Deprecated: use ParseGeneric instead. func DecodeGeneric(b []byte) (*Generic, error) { log.Println("DecodeGeneric is deprecated. use ParseGeneric instead") return ParseGeneric(b) } // DecodeFromBytes decodes bytes as Generic. // // Deprecated: use Generic.UnmarshalBinary instead. func (g *Generic) DecodeFromBytes(b []byte) error { log.Println("Generic.DecodeFromBytes is deprecated. use Generic.UnmarshalBinary instead") return g.UnmarshalBinary(b) } // Len returns the actual length of Generic. // // Deprecated: use Generic.MarshalLen instead. func (g *Generic) Len() int { log.Println("Generic.Len is deprecated. use Generic.MarshalLen instead") return g.MarshalLen() } ================================================ FILE: gtpv1/message/generic_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestGeneric(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewGeneric(message.MsgTypeEchoRequest, 0, 0), Serialized: []byte{ 0x32, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseGeneric(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/header.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "encoding/binary" "fmt" ) // Header is a GTPv1 common header. type Header struct { Flags uint8 Type uint8 Length uint16 TEID uint32 SequenceNumber uint16 NPDUNumber uint8 NextExtensionHeaderType uint8 ExtensionHeaders []*ExtensionHeader Payload []byte } // NewHeader creates a new Header. func NewHeader(flags, mtype uint8, teid uint32, seqnum uint16, payload []byte) *Header { h := &Header{ Flags: flags, Type: mtype, TEID: teid, SequenceNumber: seqnum, NextExtensionHeaderType: ExtHeaderTypeNoMoreExtensionHeaders, Payload: payload, } h.SetLength() return h } // NewHeaderWithNPDUNumber creates a new Header with NPDUNumber. func NewHeaderWithNPDUNumber(flags, mtype uint8, teid uint32, seqnum uint16, npdu uint8, payload []byte) *Header { h := NewHeader(flags, mtype, teid, seqnum, payload) h.SetNPDUNumber(npdu) return h } // NewHeaderWithExtensionHeaders creates a new Header with ExtensionHeaders. func NewHeaderWithExtensionHeaders(flags, mtype uint8, teid uint32, seqnum uint16, payload []byte, extHdrs ...*ExtensionHeader) *Header { h := NewHeader(flags, mtype, teid, seqnum, payload) _ = h.AddExtensionHeaders(extHdrs...) return h } // NewHeaderFlags returns a Header Flag built by its components given as arguments. func NewHeaderFlags(v, p, e, s, n int) uint8 { return uint8( ((v & 0x7) << 5) | ((p & 0x1) << 4) | ((e & 0x1) << 2) | ((s & 0x1) << 1) | (n & 0x1), ) } // Marshal returns the byte sequence generated from a Header. func (h *Header) Marshal() ([]byte, error) { b := make([]byte, h.MarshalLen()) if err := h.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (h *Header) MarshalTo(b []byte) error { if len(b) < h.MarshalLen() { return ErrTooShortToMarshal } b[0] = h.Flags b[1] = h.Type binary.BigEndian.PutUint16(b[2:4], h.Length) binary.BigEndian.PutUint32(b[4:8], h.TEID) offset := 8 s, pn, e := h.HasSequence(), h.HasNPDUNumber(), h.HasExtensionHeader() if s { binary.BigEndian.PutUint16(b[offset:offset+2], h.SequenceNumber) } if pn { b[offset+2] = h.NPDUNumber } if e { b[offset+3] = h.NextExtensionHeaderType } if s || pn || e { offset += 4 } if e { for _, eh := range h.ExtensionHeaders { if err := eh.MarshalTo(b[offset:]); err != nil { return err } offset += eh.MarshalLen() } } copy(b[offset:], h.Payload) return nil } // ParseHeader decodes given byte sequence as a GTPv1 header. func ParseHeader(b []byte) (*Header, error) { h := &Header{} if err := h.UnmarshalBinary(b); err != nil { return nil, err } return h, nil } // UnmarshalBinary sets the values retrieved from byte sequence in GTPv1 header. func (h *Header) UnmarshalBinary(b []byte) error { l := len(b) if l < 8 { return ErrTooShortToParse } var offset = 4 h.Flags = b[0] h.Type = b[1] h.Length = binary.BigEndian.Uint16(b[2:4]) h.TEID = binary.BigEndian.Uint32(b[4:8]) offset += 4 if l < int(h.Length)+8 { return ErrTooShortToParse } s, pn, e := h.HasSequence(), h.HasNPDUNumber(), h.HasExtensionHeader() // The optional 4 bytes will append to mandatory GTP header if any one or more S, E flags are set. if s || pn || e { if h.Length < 4 { return ErrTooShortToParse } // Sequence number must be interpreted only if the S bit is on if s { h.SequenceNumber = binary.BigEndian.Uint16(b[offset : offset+2]) } // NPDUNumber must be interpreted only if the PN bit is on if pn { h.NPDUNumber = b[offset+2] } // Next Extension Header Type must be interpreted only if the E bit is on. if e { h.NextExtensionHeaderType = b[offset+3] } offset += 4 } if e { var err error h.ExtensionHeaders, err = ParseMultiExtensionHeaders(b[offset:]) if err != nil { return err } h.ExtensionHeaders[0].Type = h.NextExtensionHeaderType for _, eh := range h.ExtensionHeaders { offset += eh.MarshalLen() } } if offset > int(h.Length)+8 { return ErrInvalidLength } h.Payload = b[offset : 8+h.Length] return nil } // SetTEID sets the TEIDFlag to 1 and puts the TEID given into TEID field. func (h *Header) SetTEID(teid uint32) { h.Flags |= (1 << 3) h.TEID = teid } // Sequence returns SequenceNumber in uint16. func (h *Header) Sequence() uint16 { return h.SequenceNumber } // HasSequence reports whether a Header has SequenceNumber by checking the flag. func (h *Header) HasSequence() bool { return ((int(h.Flags) >> 1) & 0x1) == 1 } // SetSequenceNumber sets the SequenceNumber in Header. func (h *Header) SetSequenceNumber(seq uint16) { h.Flags |= 0x02 h.SequenceNumber = seq h.SetLength() } // WithSequenceNumber returns the Header with SequenceNumber added. func (h *Header) WithSequenceNumber(seq uint16) *Header { h.SetSequenceNumber(seq) return h } // HasNPDUNumber reports whether a Header has N-PDU Number by checking the flag. func (h *Header) HasNPDUNumber() bool { return (int(h.Flags) & 0x1) == 1 } // SetNPDUNumber sets the NPDUNumber in Header. func (h *Header) SetNPDUNumber(npdu uint8) { h.Flags |= 0x01 h.NPDUNumber = npdu h.SetLength() } // WithNPDUNumber returns the Header with NPDUNumber added. func (h *Header) WithNPDUNumber(npdu uint8) *Header { h.SetNPDUNumber(npdu) return h } // HasExtensionHeader reports whether a Header has extension header by checking the flag. func (h *Header) HasExtensionHeader() bool { return ((int(h.Flags) >> 2) & 0x1) == 1 } // SetNextExtensionHeaderType sets the ExtensionHeaderType in Header. func (h *Header) SetNextExtensionHeaderType(exhType uint8) { h.Flags |= 0x04 h.NextExtensionHeaderType = exhType } // WithExtensionHeaders returns the Header with ExtensionHeaders added. func (h *Header) WithExtensionHeaders(extHdrs ...*ExtensionHeader) *Header { _ = h.AddExtensionHeaders(extHdrs...) return h } // MarshalLen returns the serial length of Header. func (h *Header) MarshalLen() int { l := len(h.Payload) + 8 if h.HasSequence() || h.HasNPDUNumber() || h.HasExtensionHeader() { l += 4 } if h.HasExtensionHeader() { for _, eh := range h.ExtensionHeaders { l += eh.MarshalLen() } } return l } // SetLength sets the length in Length field. func (h *Header) SetLength() { h.Length = uint16(h.MarshalLen() - 8) } // Version returns GTP version in int. func (h *Header) Version() int { return 1 } // MessageType returns the type of message. func (h *Header) MessageType() uint8 { return h.Type } // String returns the GTPv1 header values in human readable format. func (h *Header) String() string { return fmt.Sprintf( "{Flags: %#x, Type: %#x, Length: %d, TEID: %#08x, SequenceNumber: %#04x, NPDUNumber: %#x, "+ "ExtensionHeaderType: %#x, ExtensionHeaders: %v, Payload: %#v}", h.Flags, h.Type, h.Length, h.TEID, h.SequenceNumber, h.NPDUNumber, h.NextExtensionHeaderType, h.ExtensionHeaders, h.Payload, ) } // AddExtensionHeaders adds ExtensionHeader(s) to Header. // // This function validates if the next extension header type matches the actual one for safety. // To create arbitrary(possibly malformed) Header, access ExtensionHeaders field on your own. func (h *Header) AddExtensionHeaders(extHdrs ...*ExtensionHeader) error { if len(extHdrs) < 1 { return nil } h.Flags |= 0x04 h.NextExtensionHeaderType = extHdrs[0].Type next := h.NextExtensionHeaderType for _, eh := range extHdrs { if next != eh.Type { return fmt.Errorf("next type: %x does not match the current type: %x", next, eh.Type) } h.ExtensionHeaders = append(h.ExtensionHeaders, eh) next = eh.NextType } if next != ExtHeaderTypeNoMoreExtensionHeaders { return fmt.Errorf("non-empty next type: %x is specified but does not exist", next) } h.SetLength() return nil } ================================================ FILE: gtpv1/message/header_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes Header into bytes. // // Deprecated: use Header.Marshal instead. func (h *Header) Serialize() ([]byte, error) { log.Println("Header.Serialize is deprecated. use Header.Marshal instead") return h.Marshal() } // SerializeTo serializes Header into bytes given as b. // // Deprecated: use Header.MarshalTo instead. func (h *Header) SerializeTo(b []byte) error { log.Println("Header.SerializeTo is deprecated. use Header.MarshalTo instead") return h.MarshalTo(b) } // DecodeHeader decodes bytes as Header. // // Deprecated: use ParseHeader instead. func DecodeHeader(b []byte) (*Header, error) { log.Println("DecodeHeader is deprecated. use ParseHeader instead") return ParseHeader(b) } // DecodeFromBytes decodes bytes as Header. // // Deprecated: use Header.UnmarshalBinary instead. func (h *Header) DecodeFromBytes(b []byte) error { log.Println("Header.DecodeFromBytes is deprecated. use Header.UnmarshalBinary instead") return h.UnmarshalBinary(b) } // Len returns the actual length of Header. // // Deprecated: use Header.MarshalLen instead. func (h *Header) Len() int { log.Println("Header.Len is deprecated. use Header.MarshalLen instead") return h.MarshalLen() } ================================================ FILE: gtpv1/message/header_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestHeader(t *testing.T) { cases := []testutils.TestCase{ { Description: "NoOptionals", Structured: message.NewHeader( message.NewHeaderFlags( 1, // version 1, // Protocol Type 0, // Next Extension Header? 0, // Sequence Number? 0, // N-PDU Number? ), // Flags message.MsgTypeTPDU, // Message type 0xdeadbeef, // TEID 0x00, // Sequence Number []byte{ // Payload 0xde, 0xad, 0xbe, 0xef, }, ), Serialized: []byte{ 0x30, 0xff, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, }, }, { Description: "WithSequenceNumber", Structured: message.NewHeader( message.NewHeaderFlags( 1, // version 1, // Protocol Type 0, // Next Extension Header? 1, // Sequence Number? 0, // N-PDU Number? ), // Flags message.MsgTypeEchoRequest, // Message type 0xdeadbeef, // TEID 0xcafe, // Sequence Number []byte{ // Payload 0xde, 0xad, 0xbe, 0xef, }, ), Serialized: []byte{ 0x32, 0x01, 0x00, 0x08, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, }, }, { Description: "WithNPDUNumber", Structured: message.NewHeader( message.NewHeaderFlags( 1, // version 1, // Protocol Type 0, // Next Extension Header? 0, // Sequence Number? 0, // N-PDU Number?: set to zero at first, set by With... method ), // Flags message.MsgTypeEchoRequest, // Message type 0xdeadbeef, // TEID 0x00, // Sequence Number []byte{ // Payload 0xde, 0xad, 0xbe, 0xef, }, ).WithNPDUNumber(0xff), Serialized: []byte{ 0x31, 0x01, 0x00, 0x08, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0xff, 0x00, 0xde, 0xad, 0xbe, 0xef, }, }, { Description: "WithExtensionHeaders", Structured: message.NewHeader( message.NewHeaderFlags( 1, // version 1, // Protocol Type 0, // Next Extension Header?: set to zero at first, set by With... method 0, // Sequence Number? 0, // N-PDU Number? ), // Flags message.MsgTypeEchoRequest, // Message type 0xdeadbeef, // TEID 0x00, // Sequence Number []byte{ // Payload 0xde, 0xad, 0xbe, 0xef, }, ).WithExtensionHeaders( message.NewExtensionHeader( message.ExtHeaderTypeUDPPort, []byte{0x22, 0xb8}, message.ExtHeaderTypeUDPPort, ), message.NewExtensionHeader( message.ExtHeaderTypeUDPPort, []byte{0x22, 0xb8}, message.ExtHeaderTypeNoMoreExtensionHeaders, ), ), Serialized: []byte{ 0x34, 0x01, 0x00, 0x10, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x40, 0x01, 0x22, 0xb8, 0x40, 0x01, 0x22, 0xb8, 0x00, 0xde, 0xad, 0xbe, 0xef, }, }, { Description: "WithSequenceAndNPDUNumber", Structured: message.NewHeader( message.NewHeaderFlags( 1, // version 1, // Protocol Type 0, // Next Extension Header? 0, // Sequence Number? 0, // N-PDU Number?: set to zero at first, set by With... method ), // Flags message.MsgTypeEchoRequest, // Message type 0xdeadbeef, // TEID 0x00, // Sequence Number []byte{ // Payload 0xde, 0xad, 0xbe, 0xef, }, ).WithSequenceNumber(0xcafe).WithNPDUNumber(0xff), Serialized: []byte{ 0x33, 0x01, 0x00, 0x08, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xff, 0x00, 0xde, 0xad, 0xbe, 0xef, }, }, { Description: "WithSequenceAndExtensionHeaders", Structured: message.NewHeader( message.NewHeaderFlags( 1, // version 1, // Protocol Type 0, // Next Extension Header?: set to zero at first, set by With... method 0, // Sequence Number?: set to zero at first, set by With... method 0, // N-PDU Number? ), // Flags message.MsgTypeEchoRequest, // Message type 0xdeadbeef, // TEID 0x00, // Sequence Number []byte{ // Payload 0xde, 0xad, 0xbe, 0xef, }, ).WithSequenceNumber(0xcafe).WithExtensionHeaders( message.NewExtensionHeader( message.ExtHeaderTypeUDPPort, []byte{0x022, 0xb8}, message.ExtHeaderTypeUDPPort, ), message.NewExtensionHeader( message.ExtHeaderTypeUDPPort, []byte{0x022, 0xb8}, message.ExtHeaderTypeNoMoreExtensionHeaders, ), ), Serialized: []byte{ 0x36, 0x01, 0x00, 0x10, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x00, 0x40, 0x01, 0x22, 0xb8, 0x40, 0x01, 0x22, 0xb8, 0x00, 0xde, 0xad, 0xbe, 0xef, }, }, { Description: "WithSequenceAndNPDUAndExtensionHeaders", Structured: message.NewHeader( message.NewHeaderFlags( 1, // version 1, // Protocol Type 0, // Next Extension Header?: set to zero at first, set by With... method 0, // Sequence Number?: set to zero at first, set by With... method 0, // N-PDU Number?: set to zero at first, set by With... method ), // Flags message.MsgTypeEchoRequest, // Message type 0xdeadbeef, // TEID 0x00, // Sequence Number []byte{ // Payload 0xde, 0xad, 0xbe, 0xef, }, ).WithNPDUNumber(0xff).WithSequenceNumber(0xcafe).WithExtensionHeaders( message.NewExtensionHeader( message.ExtHeaderTypeUDPPort, []byte{0x022, 0xb8}, message.ExtHeaderTypeUDPPort, ), message.NewExtensionHeader( message.ExtHeaderTypeUDPPort, []byte{0x022, 0xb8}, message.ExtHeaderTypeNoMoreExtensionHeaders, ), ), Serialized: []byte{ 0x37, 0x01, 0x00, 0x10, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xff, 0x40, 0x01, 0x22, 0xb8, 0x40, 0x01, 0x22, 0xb8, 0x00, 0xde, 0xad, 0xbe, 0xef, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseHeader(b) if err != nil { return nil, err } return v, nil }) } func TestHeaderErrorDetection(t *testing.T) { cases := []struct { Description string Serialized []byte Error error }{ { Description: "ExtFlagSetButMissingExt", Serialized: []byte{ 0b00110110, // Version (3 bits), PT, (*), E, S, PN 0x03, // Message Type 0x00, 0x04, // Length (2 octets) 0x00, 0x00, 0x00, 0x00, // TEID (4 octets) 0x00, 0x06, // Seqence Number (2 octets) 0xff, // N-PDU Number (to be ignored) 0b11000001, // Next Extension Header Type // Extension Header would go here, but is missing }, Error: message.ErrTooShortToParse, // Expect too short to parse error, as missing ext header has length 0 }, { Description: "IncorrectLength", Serialized: []byte{ 0b00110010, // Version (3 bits), PT, (*), E, S, PN 0x03, // Message Type 0x30, 0x33, // Length (2 octets) 0x00, 0x00, 0x00, 0x00, // TEID (4 octets) 0x00, 0x06, // Seqence Number (2 octets) 0xff, // N-PDU Number (to be ignored) 0b00000000, // Next Extension Header Type }, Error: message.ErrTooShortToParse, // Expect too short to parse error, as missing ext header has length 0 }, } for _, c := range cases { t.Run(c.Description, func(t *testing.T) { _, err := message.ParseHeader(c.Serialized) if err != c.Error { t.Fatalf("got '%v' want '%v'", err, c.Error) } }) } } ================================================ FILE: gtpv1/message/message.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. /* Package message provides encoding/decoding feature of GTPv1 protocol. */ package message import ( "fmt" "reflect" "strings" "github.com/wmnsk/go-gtp/gtpv1/ie" ) // Message Type definitions. const ( _ uint8 = iota MsgTypeEchoRequest MsgTypeEchoResponse MsgTypeVersionNotSupported MsgTypeNodeAliveRequest MsgTypeNodeAliveResponse MsgTypeRedirectionRequest MsgTypeRedirectionResponse _ _ _ _ _ _ _ _ MsgTypeCreatePDPContextRequest // 16 MsgTypeCreatePDPContextResponse MsgTypeUpdatePDPContextRequest MsgTypeUpdatePDPContextResponse MsgTypeDeletePDPContextRequest MsgTypeDeletePDPContextResponse MsgTypeCreateAAPDPContextRequest MsgTypeCreateAAPDPContextResponse MsgTypeDeleteAAPDPContextRequest MsgTypeDeleteAAPDPContextResponse MsgTypeErrorIndication MsgTypePDUNotificationRequest MsgTypePDUNotificationResponse MsgTypePDUNotificationRejectRequest MsgTypePDUNotificationRejectResponse MsgTypeSupportedExtensionHeaderNotification MsgTypeSendRoutingInfoRequest MsgTypeSendRoutingInfoResponse MsgTypeFailureReportRequest MsgTypeFailureReportResponse MsgTypeNoteMSPresentRequest MsgTypeNoteMSPresentResponse _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ MsgTypeIdentificationRequest // 48 MsgTypeIdentificationResponse MsgTypeSGSNContextRequest MsgTypeSGSNContextResponse MsgTypeSGSNContextAcknowledge MsgTypeDataRecordTransferRequest uint8 = 240 MsgTypeDataRecordTransferResponse uint8 = 241 MsgTypeEndMarker uint8 = 254 MsgTypeTPDU uint8 = 255 ) // Message is an interface that defines Message message. type Message interface { MarshalTo([]byte) error UnmarshalBinary(b []byte) error MarshalLen() int Version() int MessageType() uint8 MessageTypeName() string TEID() uint32 SetTEID(uint32) Sequence() uint16 SetSequenceNumber(uint16) // deprecated SerializeTo([]byte) error DecodeFromBytes(b []byte) error } // Marshal returns the byte sequence generated from a Message instance. // Better to use MarshalXxx instead if you know the name of message to be serialized. func Marshal(g Message) ([]byte, error) { b := make([]byte, g.MarshalLen()) if err := g.MarshalTo(b); err != nil { return nil, err } return b, nil } // Parse parses the given bytes as a Message. func Parse(b []byte) (Message, error) { if len(b) < 2 { return nil, ErrTooShortToParse } var m Message switch b[1] { case MsgTypeEchoRequest: m = &EchoRequest{} case MsgTypeEchoResponse: m = &EchoResponse{} case MsgTypeCreatePDPContextRequest: m = &CreatePDPContextRequest{} case MsgTypeCreatePDPContextResponse: m = &CreatePDPContextResponse{} case MsgTypeUpdatePDPContextRequest: m = &UpdatePDPContextRequest{} case MsgTypeUpdatePDPContextResponse: m = &UpdatePDPContextResponse{} case MsgTypeDeletePDPContextRequest: m = &DeletePDPContextRequest{} case MsgTypeVersionNotSupported: m = &VersionNotSupported{} case MsgTypeDeletePDPContextResponse: m = &DeletePDPContextResponse{} /* TODO: Implement! case MsgTypeNodeAliveRequest: m = &NodeAliveReq{} case MsgTypeNodeAliveResponse: m = &NodeAliveRes{} case MsgTypeRedirectionRequest: m = &RedirectionReq{} case MsgTypeRedirectionResponse: m = &RedirectionRes{} case MsgTypeCreateAaPDPContextRequest: m = &CreateAaPDPContextReq{} case MsgTypeCreateAaPDPContextResponse: m = &CreateAaPDPContextRes{} case MsgTypeDeleteAaPDPContextRequest: m = &DeleteAaPDPContextReq{} case MsgTypeDeleteAaPDPContextResponse: m = &DeleteAaPDPContextRes{} */ case MsgTypeErrorIndication: m = &ErrorIndication{} /* TODO: Implement! case MsgTypePduNotificationRequest: m = &PduNotificationReq{} case MsgTypePduNotificationResponse: m = &PduNotificationRes{} case MsgTypePduNotificationRejectRequest: m = &PduNotificationRejectReq{} case MsgTypePduNotificationRejectResponse: m = &PduNotificationRejectRes{} */ case MsgTypeSupportedExtensionHeaderNotification: m = &SupportedExtensionHeaderNotification{} /* TODO: Implement! case MsgTypeSendRoutingInfoRequest: m = &SendRoutingInfoReq{} case MsgTypeSendRoutingInfoResponse: m = &SendRoutingInfoRes{} case MsgTypeFailureReportRequest: m = &FailureReportReq{} case MsgTypeFailureReportResponse: m = &FailureReportRes{} case MsgTypeNoteMsPresentRequest: m = &NoteMsPresentReq{} case MsgTypeNoteMsPresentResponse: m = &NoteMsPresentRes{} case MsgTypeIdentificationRequest: m = &IdentificationReq{} case MsgTypeIdentificationResponse: m = &IdentificationRes{} case MsgTypeSgsnContextRequest: m = &SgsnContextReq{} case MsgTypeSgsnContextResponse: m = &SgsnContextRes{} case MsgTypeSgsnContextAcknowledge: m = &SgsnContextAck{} case MsgTypeDataRecordTransferRequest: m = &DataRecordTransferReq{} case MsgTypeDataRecordTransferResponse: m = &DataRecordTransferRes{} */ case MsgTypeEndMarker: m = &EndMarker{} case MsgTypeTPDU: m = &TPDU{} default: m = &Generic{} } if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // Prettify returns a Message in prettified representation in string. // // Note that this relies much on reflect package, and thus the frequent use of // this function may have a serious impact on the performance of your software. func Prettify(m Message) string { name := m.MessageTypeName() header := strings.TrimSuffix(fmt.Sprint(m), "}") v := reflect.Indirect(reflect.ValueOf(m)) n := v.NumField() - 1 fields := make([]*field, n) for i := 1; i < n+1; i++ { // Skip *Header fields[i-1] = &field{name: v.Type().Field(i).Name, maybeIE: v.Field(i).Interface()} } return fmt.Sprintf("{%s: %s, IEs: [%v]}", name, header, strings.Join(prettifyFields(fields), ", ")) } type field struct { name string maybeIE interface{} } func prettifyFields(fields []*field) []string { ret := []string{} for _, field := range fields { if field.maybeIE == nil { ret = append(ret, prettifyIE(field.name, nil)) continue } // TODO: do this recursively? v, ok := field.maybeIE.(*ie.IE) if !ok { // only for AdditionalIEs field if ies, ok := field.maybeIE.([]*ie.IE); ok { vals := make([]string, len(ies)) for i, val := range ies { vals[i] = fmt.Sprint(val) } ret = append(ret, fmt.Sprintf("{%s: [%v]}", field.name, strings.Join(vals, ", "))) } continue } ret = append(ret, prettifyIE(field.name, v)) } return ret } func prettifyIE(name string, i *ie.IE) string { return fmt.Sprintf("{%s: %v}", name, i) } ================================================ FILE: gtpv1/message/message_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes Message into bytes. // // Deprecated: use Marshal instead. func Serialize(m Message) ([]byte, error) { log.Println("Serialize is deprecated. use Marshal instead") return Marshal(m) } // Decode decodes bytes as Message. // // Deprecated: use Parse instead. func Decode(b []byte) (Message, error) { log.Println("Decode is deprecated. use Parse instead") return Parse(b) } ================================================ FILE: gtpv1/message/message_fuzz_test.go ================================================ package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/message" ) func FuzzParse(f *testing.F) { f.Fuzz(func(t *testing.T, b []byte) { if _, err := message.Parse(b); err != nil { t.Skip() } }) } func FuzzHeaderParse(f *testing.F) { f.Add([]byte("10000000")) f.Add([]byte("70\x00\x0400000000\x000")) f.Add([]byte("70\x00\x0400000000\x0100\x00")) f.Fuzz(func(t *testing.T, pkt []byte) { header, err := message.ParseHeader(pkt) if header == nil && err == nil { t.Errorf("nil without error") } }) } ================================================ FILE: gtpv1/message/supported-extension-header-notification.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // SupportedExtensionHeaderNotification is a SupportedExtensionHeaderNotification Header and its IEs above. type SupportedExtensionHeaderNotification struct { *Header ExtensionHeaderTypeList *ie.IE AdditionalIEs []*ie.IE } // NewSupportedExtensionHeaderNotification creates a new GTPv1 SupportedExtensionHeaderNotification. func NewSupportedExtensionHeaderNotification(teid uint32, seq uint16, ies ...*ie.IE) *SupportedExtensionHeaderNotification { e := &SupportedExtensionHeaderNotification{ Header: NewHeader(0x30, MsgTypeSupportedExtensionHeaderNotification, teid, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.ExtensionHeaderTypeList: e.ExtensionHeaderTypeList = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } e.SetLength() return e } // Marshal returns the byte sequence generated from a SupportedExtensionHeaderNotification. func (e *SupportedExtensionHeaderNotification) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *SupportedExtensionHeaderNotification) MarshalTo(b []byte) error { if len(b) < e.MarshalLen() { return ErrTooShortToMarshal } e.Header.Payload = make([]byte, e.MarshalLen()-e.Header.MarshalLen()) offset := 0 if ie := e.ExtensionHeaderTypeList; ie != nil { if err := ie.MarshalTo(e.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } e.Header.SetLength() return e.Header.MarshalTo(b) } // ParseSupportedExtensionHeaderNotification decodes a given byte sequence as a SupportedExtensionHeaderNotification. func ParseSupportedExtensionHeaderNotification(b []byte) (*SupportedExtensionHeaderNotification, error) { e := &SupportedExtensionHeaderNotification{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary decodes a given byte sequence as a SupportedExtensionHeaderNotification. func (e *SupportedExtensionHeaderNotification) UnmarshalBinary(b []byte) error { var err error e.Header, err = ParseHeader(b) if err != nil { return err } if len(e.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(e.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.ExtensionHeaderTypeList: e.ExtensionHeaderTypeList = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (e *SupportedExtensionHeaderNotification) MarshalLen() int { l := e.Header.MarshalLen() - len(e.Header.Payload) if ie := e.ExtensionHeaderTypeList; ie != nil { l += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (e *SupportedExtensionHeaderNotification) SetLength() { e.Length = uint16(e.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (e *SupportedExtensionHeaderNotification) MessageTypeName() string { return "Supported Extension Header Notification" } // TEID returns the TEID in human-readable string. func (e *SupportedExtensionHeaderNotification) TEID() uint32 { return e.Header.TEID } ================================================ FILE: gtpv1/message/supported-extension-header-notification_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestSupportedExtensionHeaderNotification(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewSupportedExtensionHeaderNotification( testutils.TestBearerInfo.TEID, 0, ie.NewExtensionHeaderTypeList( message.ExtHeaderTypePDUSessionContainer, message.ExtHeaderTypeUDPPort, ), ), Serialized: []byte{ // Header 0x30, 0x1f, 0x00, 0x04, 0x11, 0x22, 0x33, 0x44, // ExtensionHeaderTypeList 0x8d, 0x02, 0x85, 0x40, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseSupportedExtensionHeaderNotification(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/t-pdu.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message // TPDU is a TPDU. type TPDU struct { *Header } // NewTPDU creates a new T-PDU message. func NewTPDU(teid uint32, payload []byte) *TPDU { t := &TPDU{Header: NewHeader(0x30, MsgTypeTPDU, teid, 0, payload)} t.SetLength() return t } // NewTPDUWithSequence creates a new T-PDU message with the given Sequence Number. func NewTPDUWithSequence(teid uint32, seq uint16, payload []byte) *TPDU { t := &TPDU{Header: NewHeader(0x32, MsgTypeTPDU, teid, seq, payload)} t.SetLength() return t } // NewTPDUWithExtentionHeader creates a new T-PDU message with the given Extension Headers. func NewTPDUWithExtentionHeader(teid uint32, payload []byte, extHdrs ...*ExtensionHeader) *TPDU { t := &TPDU{Header: NewHeaderWithExtensionHeaders(0x34, MsgTypeTPDU, teid, 0, payload, extHdrs...)} t.SetLength() return t } // Marshal returns the byte sequence generated from a TPDU. func (t *TPDU) Marshal() ([]byte, error) { b := make([]byte, t.MarshalLen()) if err := t.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (t *TPDU) MarshalTo(b []byte) error { if len(b) < t.MarshalLen() { return ErrTooShortToMarshal } t.Header.SetLength() return t.Header.MarshalTo(b) } // ParseTPDU decodes a given byte sequence as a TPDU. func ParseTPDU(b []byte) (*TPDU, error) { t := &TPDU{} if err := t.UnmarshalBinary(b); err != nil { return nil, err } return t, nil } // UnmarshalBinary decodes a given byte sequence as a TPDU. func (t *TPDU) UnmarshalBinary(b []byte) error { var err error t.Header, err = ParseHeader(b) if err != nil { return err } return nil } // MarshalLen returns the serial length of Data. func (t *TPDU) MarshalLen() int { return t.Header.MarshalLen() } // SetLength sets the length in Length field. func (t *TPDU) SetLength() { t.Header.SetLength() } // MessageTypeName returns the name of protocol. func (t *TPDU) MessageTypeName() string { return "T-PDU" } // TEID returns the TEID in human-readable string. func (t *TPDU) TEID() uint32 { return t.Header.TEID } // Decapsulate returns payload as raw []byte. func (t *TPDU) Decapsulate() []byte { return t.Header.Payload } ================================================ FILE: gtpv1/message/t-pdu_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes TPDU into bytes. // // Deprecated: use TPDU.Marshal instead. func (t *TPDU) Serialize() ([]byte, error) { log.Println("TPDU.Serialize is deprecated. use TPDU.Marshal instead") return t.Marshal() } // SerializeTo serializes TPDU into bytes given as b. // // Deprecated: use TPDU.MarshalTo instead. func (t *TPDU) SerializeTo(b []byte) error { log.Println("TPDU.SerializeTo is deprecated. use TPDU.MarshalTo instead") return t.MarshalTo(b) } // DecodeTPDU decodes bytes as TPDU. // // Deprecated: use ParseTPDU instead. func DecodeTPDU(b []byte) (*TPDU, error) { log.Println("DecodeTPDU is deprecated. use ParseTPDU instead") return ParseTPDU(b) } // DecodeFromBytes decodes bytes as TPDU. // // Deprecated: use TPDU.UnmarshalBinary instead. func (t *TPDU) DecodeFromBytes(b []byte) error { log.Println("TPDU.DecodeFromBytes is deprecated. use TPDU.UnmarshalBinary instead") return t.UnmarshalBinary(b) } // Len returns the actual length of TPDU. // // Deprecated: use TPDU.MarshalLen instead. func (t *TPDU) Len() int { log.Println("TPDU.Len is deprecated. use TPDU.MarshalLen instead") return t.MarshalLen() } ================================================ FILE: gtpv1/message/t-pdu_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestTPDU(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewTPDU(0xdeadbeef, []byte{0xde, 0xad, 0xbe, 0xef}), Serialized: []byte{ 0x30, 0xff, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, }, }, { Description: "With-Sequence", Structured: message.NewTPDUWithSequence(0xdeadbeef, 0x0001, []byte{0xde, 0xad, 0xbe, 0xef}), Serialized: []byte{ 0x32, 0xff, 0x00, 0x08, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x01, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, }, }, { Description: "With-ExtensionHeader", Structured: message.NewTPDUWithExtentionHeader( 0xdeadbeef, []byte{0xde, 0xad, 0xbe, 0xef}, message.NewExtensionHeader( message.ExtHeaderTypePDUSessionContainer, []byte{0x00, 0x05}, message.ExtHeaderTypeNoMoreExtensionHeaders, ), ), Serialized: []byte{ 0x34, 0xff, 0x00, 0x0c, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, // Next extension header type 0x85, // Extension Header 0x01, 0x00, 0x05, 0x00, // Payload 0xde, 0xad, 0xbe, 0xef, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseTPDU(b) if err != nil { return nil, err } return v, nil }) } ================================================ FILE: gtpv1/message/update-pdp-context-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserveu. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // UpdatePDPContextRequest is a UpdatePDPContextRequest Header and its IEs above. type UpdatePDPContextRequest struct { *Header IMSI *ie.IE RAI *ie.IE Recovery *ie.IE TEIDDataI *ie.IE TEIDCPlane *ie.IE NSAPI *ie.IE TraceReference *ie.IE TraceType *ie.IE PCO *ie.IE SGSNAddressForCPlane *ie.IE SGSNAddressForUserTraffic *ie.IE AlternativeSGSNAddressForCPlane *ie.IE AlternativeSGSNAddressForUserTraffic *ie.IE QoSProfile *ie.IE TFT *ie.IE TriggerID *ie.IE OMCIdentity *ie.IE CommonFlags *ie.IE RATType *ie.IE ULI *ie.IE MSTimeZone *ie.IE AdditionalTraceInfo *ie.IE DirectTunnelFlags *ie.IE EvolvedARPI *ie.IE ExtendedCommonFlags *ie.IE UCI *ie.IE APNAMBR *ie.IE SignallingPriorityIndication *ie.IE CNOperatorSelectionEntity *ie.IE IMEI *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewUpdatePDPContextRequest creates a new GTPv1 UpdatePDPContextRequest. func NewUpdatePDPContextRequest(teid uint32, seq uint16, ies ...*ie.IE) *UpdatePDPContextRequest { u := &UpdatePDPContextRequest{ Header: NewHeader(0x32, MsgTypeUpdatePDPContextRequest, teid, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: u.IMSI = i case ie.RouteingAreaIdentity: u.RAI = i case ie.Recovery: u.Recovery = i case ie.TEIDDataI: u.TEIDDataI = i case ie.TEIDCPlane: u.TEIDCPlane = i case ie.NSAPI: u.NSAPI = i case ie.TraceReference: u.TraceReference = i case ie.TraceType: u.TraceType = i case ie.ProtocolConfigurationOptions: u.PCO = i case ie.GSNAddress: if u.SGSNAddressForCPlane == nil { u.SGSNAddressForCPlane = i continue } if u.SGSNAddressForUserTraffic == nil { u.SGSNAddressForUserTraffic = i continue } if u.AlternativeSGSNAddressForCPlane == nil { u.AlternativeSGSNAddressForCPlane = i continue } u.AlternativeSGSNAddressForUserTraffic = i case ie.QoSProfile: u.QoSProfile = i case ie.TrafficFlowTemplate: u.TFT = i case ie.TriggerID: u.TriggerID = i case ie.OMCIdentity: u.OMCIdentity = i case ie.CommonFlags: u.CommonFlags = i case ie.RATType: u.RATType = i case ie.UserLocationInformation: u.ULI = i case ie.MSTimeZone: u.MSTimeZone = i case ie.AdditionalTraceInfo: u.AdditionalTraceInfo = i case ie.DirectTunnelFlags: u.DirectTunnelFlags = i case ie.EvolvedAllocationRetentionPriorityI: u.EvolvedARPI = i case ie.ExtendedCommonFlags: u.ExtendedCommonFlags = i case ie.UserCSGInformation: u.UCI = i case ie.AggregateMaximumBitRate: u.APNAMBR = i case ie.SignallingPriorityIndication: u.SignallingPriorityIndication = i case ie.CNOperatorSelectionEntity: u.CNOperatorSelectionEntity = i case ie.IMEISV: u.IMEI = i case ie.PrivateExtension: u.PrivateExtension = i default: u.AdditionalIEs = append(u.AdditionalIEs, i) } } u.SetLength() return u } // Marshal returns the byte sequence generated from a UpdatePDPContextRequest. func (u *UpdatePDPContextRequest) Marshal() ([]byte, error) { b := make([]byte, u.MarshalLen()) if err := u.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (u *UpdatePDPContextRequest) MarshalTo(b []byte) error { if len(b) < u.MarshalLen() { return ErrTooShortToMarshal } u.Header.Payload = make([]byte, u.MarshalLen()-u.Header.MarshalLen()) offset := 0 if ie := u.IMSI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.RAI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.Recovery; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.TEIDDataI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.TEIDCPlane; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.NSAPI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.TraceReference; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.TraceType; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.PCO; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.SGSNAddressForCPlane; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.SGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.AlternativeSGSNAddressForCPlane; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.AlternativeSGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.QoSProfile; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.TFT; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.TriggerID; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.OMCIdentity; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.CommonFlags; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.RATType; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.ULI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.MSTimeZone; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.AdditionalTraceInfo; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.DirectTunnelFlags; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.EvolvedARPI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.ExtendedCommonFlags; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.UCI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.APNAMBR; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.SignallingPriorityIndication; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.CNOperatorSelectionEntity; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.IMEI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.PrivateExtension; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range u.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(u.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } u.Header.SetLength() return u.Header.MarshalTo(b) } // ParseUpdatePDPContextRequest decodes a given byte sequence as a UpdatePDPContextRequest. func ParseUpdatePDPContextRequest(b []byte) (*UpdatePDPContextRequest, error) { u := &UpdatePDPContextRequest{} if err := u.UnmarshalBinary(b); err != nil { return nil, err } return u, nil } // UnmarshalBinary decodes a given byte sequence as a UpdatePDPContextRequest. func (u *UpdatePDPContextRequest) UnmarshalBinary(b []byte) error { var err error u.Header, err = ParseHeader(b) if err != nil { return err } if len(u.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(u.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: u.IMSI = i case ie.RouteingAreaIdentity: u.RAI = i case ie.Recovery: u.Recovery = i case ie.TEIDDataI: u.TEIDDataI = i case ie.TEIDCPlane: u.TEIDCPlane = i case ie.NSAPI: u.NSAPI = i case ie.TraceReference: u.TraceReference = i case ie.TraceType: u.TraceType = i case ie.ProtocolConfigurationOptions: u.PCO = i case ie.GSNAddress: if u.SGSNAddressForCPlane == nil { u.SGSNAddressForCPlane = i continue } if u.SGSNAddressForUserTraffic == nil { u.SGSNAddressForUserTraffic = i continue } if u.AlternativeSGSNAddressForCPlane == nil { u.AlternativeSGSNAddressForCPlane = i continue } u.AlternativeSGSNAddressForUserTraffic = i case ie.QoSProfile: u.QoSProfile = i case ie.TrafficFlowTemplate: u.TFT = i case ie.TriggerID: u.TriggerID = i case ie.OMCIdentity: u.OMCIdentity = i case ie.CommonFlags: u.CommonFlags = i case ie.RATType: u.RATType = i case ie.UserLocationInformation: u.ULI = i case ie.MSTimeZone: u.MSTimeZone = i case ie.AdditionalTraceInfo: u.AdditionalTraceInfo = i case ie.DirectTunnelFlags: u.DirectTunnelFlags = i case ie.EvolvedAllocationRetentionPriorityI: u.EvolvedARPI = i case ie.ExtendedCommonFlags: u.ExtendedCommonFlags = i case ie.UserCSGInformation: u.UCI = i case ie.AggregateMaximumBitRate: u.APNAMBR = i case ie.SignallingPriorityIndication: u.SignallingPriorityIndication = i case ie.CNOperatorSelectionEntity: u.CNOperatorSelectionEntity = i case ie.IMEISV: u.IMEI = i case ie.PrivateExtension: u.PrivateExtension = i default: u.AdditionalIEs = append(u.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (u *UpdatePDPContextRequest) MarshalLen() int { l := u.Header.MarshalLen() - len(u.Header.Payload) if ie := u.IMSI; ie != nil { l += ie.MarshalLen() } if ie := u.RAI; ie != nil { l += ie.MarshalLen() } if ie := u.Recovery; ie != nil { l += ie.MarshalLen() } if ie := u.TEIDDataI; ie != nil { l += ie.MarshalLen() } if ie := u.TEIDCPlane; ie != nil { l += ie.MarshalLen() } if ie := u.NSAPI; ie != nil { l += ie.MarshalLen() } if ie := u.TraceReference; ie != nil { l += ie.MarshalLen() } if ie := u.TraceType; ie != nil { l += ie.MarshalLen() } if ie := u.PCO; ie != nil { l += ie.MarshalLen() } if ie := u.SGSNAddressForCPlane; ie != nil { l += ie.MarshalLen() } if ie := u.SGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := u.AlternativeSGSNAddressForCPlane; ie != nil { l += ie.MarshalLen() } if ie := u.AlternativeSGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := u.QoSProfile; ie != nil { l += ie.MarshalLen() } if ie := u.TFT; ie != nil { l += ie.MarshalLen() } if ie := u.TriggerID; ie != nil { l += ie.MarshalLen() } if ie := u.OMCIdentity; ie != nil { l += ie.MarshalLen() } if ie := u.CommonFlags; ie != nil { l += ie.MarshalLen() } if ie := u.RATType; ie != nil { l += ie.MarshalLen() } if ie := u.ULI; ie != nil { l += ie.MarshalLen() } if ie := u.MSTimeZone; ie != nil { l += ie.MarshalLen() } if ie := u.AdditionalTraceInfo; ie != nil { l += ie.MarshalLen() } if ie := u.DirectTunnelFlags; ie != nil { l += ie.MarshalLen() } if ie := u.EvolvedARPI; ie != nil { l += ie.MarshalLen() } if ie := u.ExtendedCommonFlags; ie != nil { l += ie.MarshalLen() } if ie := u.UCI; ie != nil { l += ie.MarshalLen() } if ie := u.APNAMBR; ie != nil { l += ie.MarshalLen() } if ie := u.SignallingPriorityIndication; ie != nil { l += ie.MarshalLen() } if ie := u.CNOperatorSelectionEntity; ie != nil { l += ie.MarshalLen() } if ie := u.IMEI; ie != nil { l += ie.MarshalLen() } if ie := u.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range u.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (u *UpdatePDPContextRequest) SetLength() { u.Length = uint16(u.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (u *UpdatePDPContextRequest) MessageTypeName() string { return "Update PDP Context Request" } // TEID returns the TEID in human-readable string. func (u *UpdatePDPContextRequest) TEID() uint32 { return u.Header.TEID } ================================================ FILE: gtpv1/message/update-pdp-context-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes UpdatePDPContextRequest into bytes. // // Deprecated: use UpdatePDPContextRequest.Marshal instead. func (u *UpdatePDPContextRequest) Serialize() ([]byte, error) { log.Println("UpdatePDPContextRequest.Serialize is deprecated. use UpdatePDPContextRequest.Marshal instead") return u.Marshal() } // SerializeTo serializes UpdatePDPContextRequest into bytes given as b. // // Deprecated: use UpdatePDPContextRequest.MarshalTo instead. func (u *UpdatePDPContextRequest) SerializeTo(b []byte) error { log.Println("UpdatePDPContextRequest.SerializeTo is deprecated. use UpdatePDPContextRequest.MarshalTo instead") return u.MarshalTo(b) } // DecodeUpdatePDPContextRequest decodes bytes as UpdatePDPContextRequest. // // Deprecated: use ParseUpdatePDPContextRequest instead. func DecodeUpdatePDPContextRequest(b []byte) (*UpdatePDPContextRequest, error) { log.Println("DecodeUpdatePDPContextRequest is deprecated. use ParseUpdatePDPContextRequest instead") return ParseUpdatePDPContextRequest(b) } // DecodeFromBytes decodes bytes as UpdatePDPContextRequest. // // Deprecated: use UpdatePDPContextRequest.UnmarshalBinary instead. func (u *UpdatePDPContextRequest) DecodeFromBytes(b []byte) error { log.Println("UpdatePDPContextRequest.DecodeFromBytes is deprecated. use UpdatePDPContextRequest.UnmarshalBinary instead") return u.UnmarshalBinary(b) } // Len returns the actual length of UpdatePDPContextRequest. // // Deprecated: use UpdatePDPContextRequest.MarshalLen instead. func (u *UpdatePDPContextRequest) Len() int { log.Println("UpdatePDPContextRequest.Len is deprecated. use UpdatePDPContextRequest.MarshalLen instead") return u.MarshalLen() } ================================================ FILE: gtpv1/message/update-pdp-context-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestUpdatePDPContextRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewUpdatePDPContextRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIMSI("123450123456789"), ie.NewTEIDDataI(0xdeadbeef), ie.NewTEIDCPlane(0xdeadbeef), ie.NewNSAPI(5), ie.NewGSNAddress("1.1.1.1"), ie.NewGSNAddress("2.2.2.2"), ), Serialized: []byte{ // Header 0x32, 0x12, 0x00, 0x27, 0x11, 0x22, 0x33, 0x44, 0x00, 0x01, 0x00, 0x00, // IMSI 0x02, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9, // TEID-U 0x10, 0xde, 0xad, 0xbe, 0xef, // TEID-C 0x11, 0xde, 0xad, 0xbe, 0xef, // NSAPI 0x14, 0x05, // GSN Address 0x85, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, // GSN Address 0x85, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseUpdatePDPContextRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/update-pdp-context-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserveu. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // UpdatePDPContextResponse is a UpdatePDPContextResponse Header and its IEs above. type UpdatePDPContextResponse struct { *Header Cause *ie.IE Recovery *ie.IE TEIDDataI *ie.IE TEIDCPlane *ie.IE ChargingID *ie.IE PCO *ie.IE GGSNAddressForCPlane *ie.IE GGSNAddressForUserTraffic *ie.IE AltGGSNAddressForCPlane *ie.IE AltGGSNAddressForUserTraffic *ie.IE QoSProfile *ie.IE ChargingGatewayAddress *ie.IE AltChargingGatewayAddress *ie.IE CommonFlags *ie.IE APNRestriction *ie.IE BearerControlMode *ie.IE MSInfoChangeReportingAction *ie.IE EvolvedARPI *ie.IE CSGInformationReportingAction *ie.IE APNAMBR *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewUpdatePDPContextResponse creates a new GTPv1 UpdatePDPContextResponse. func NewUpdatePDPContextResponse(teid uint32, seq uint16, ies ...*ie.IE) *UpdatePDPContextResponse { u := &UpdatePDPContextResponse{ Header: NewHeader(0x32, MsgTypeUpdatePDPContextResponse, teid, seq, nil), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: u.Cause = i case ie.Recovery: u.Recovery = i case ie.TEIDDataI: u.TEIDDataI = i case ie.TEIDCPlane: u.TEIDCPlane = i case ie.ChargingID: u.ChargingID = i case ie.ProtocolConfigurationOptions: u.PCO = i case ie.GSNAddress: if u.GGSNAddressForCPlane == nil { u.GGSNAddressForCPlane = i continue } if u.GGSNAddressForUserTraffic == nil { u.GGSNAddressForUserTraffic = i continue } if u.AltGGSNAddressForCPlane == nil { u.AltGGSNAddressForCPlane = i continue } u.AltGGSNAddressForUserTraffic = i case ie.QoSProfile: u.QoSProfile = i case ie.ChargingGatewayAddress: if u.ChargingGatewayAddress == nil { u.ChargingGatewayAddress = i } else if u.AltChargingGatewayAddress == nil { u.AltChargingGatewayAddress = i } case ie.CommonFlags: u.CommonFlags = i case ie.APNRestriction: u.APNRestriction = i case ie.BearerControlMode: u.BearerControlMode = i case ie.MSInfoChangeReportingAction: u.MSInfoChangeReportingAction = i case ie.EvolvedAllocationRetentionPriorityI: u.EvolvedARPI = i case ie.CSGInformationReportingAction: u.CSGInformationReportingAction = i case ie.AggregateMaximumBitRate: u.APNAMBR = i case ie.PrivateExtension: u.PrivateExtension = i default: u.AdditionalIEs = append(u.AdditionalIEs, i) } } u.SetLength() return u } // Marshal returns the byte sequence generated from a UpdatePDPContextResponse. func (u *UpdatePDPContextResponse) Marshal() ([]byte, error) { b := make([]byte, u.MarshalLen()) if err := u.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (u *UpdatePDPContextResponse) MarshalTo(b []byte) error { if len(b) < u.MarshalLen() { return ErrTooShortToMarshal } u.Header.Payload = make([]byte, u.MarshalLen()-u.Header.MarshalLen()) offset := 0 if ie := u.Cause; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.Recovery; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.TEIDDataI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.TEIDCPlane; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.ChargingID; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.PCO; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.GGSNAddressForCPlane; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.GGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.AltGGSNAddressForCPlane; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.AltGGSNAddressForUserTraffic; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.QoSProfile; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.ChargingGatewayAddress; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.AltChargingGatewayAddress; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.CommonFlags; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.APNRestriction; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.BearerControlMode; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.MSInfoChangeReportingAction; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.EvolvedARPI; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.CSGInformationReportingAction; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.APNAMBR; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := u.PrivateExtension; ie != nil { if err := ie.MarshalTo(u.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range u.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(u.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } u.Header.SetLength() return u.Header.MarshalTo(b) } // ParseUpdatePDPContextResponse decodes a given byte sequence as a UpdatePDPContextResponse. func ParseUpdatePDPContextResponse(b []byte) (*UpdatePDPContextResponse, error) { u := &UpdatePDPContextResponse{} if err := u.UnmarshalBinary(b); err != nil { return nil, err } return u, nil } // UnmarshalBinary decodes a given byte sequence as a UpdatePDPContextResponse. func (u *UpdatePDPContextResponse) UnmarshalBinary(b []byte) error { var err error u.Header, err = ParseHeader(b) if err != nil { return err } if len(u.Header.Payload) < 2 { return nil } ies, err := ie.ParseMultiIEs(u.Header.Payload) if err != nil { return err } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: u.Cause = i case ie.Recovery: u.Recovery = i case ie.TEIDDataI: u.TEIDDataI = i case ie.TEIDCPlane: u.TEIDCPlane = i case ie.ChargingID: u.ChargingID = i case ie.ProtocolConfigurationOptions: u.PCO = i case ie.GSNAddress: if u.GGSNAddressForCPlane == nil { u.GGSNAddressForCPlane = i continue } if u.GGSNAddressForUserTraffic == nil { u.GGSNAddressForUserTraffic = i continue } if u.AltGGSNAddressForCPlane == nil { u.AltGGSNAddressForCPlane = i continue } u.AltGGSNAddressForUserTraffic = i case ie.QoSProfile: u.QoSProfile = i case ie.ChargingGatewayAddress: if u.ChargingGatewayAddress == nil { u.ChargingGatewayAddress = i } else if u.AltChargingGatewayAddress == nil { u.AltChargingGatewayAddress = i } case ie.CommonFlags: u.CommonFlags = i case ie.APNRestriction: u.APNRestriction = i case ie.BearerControlMode: u.BearerControlMode = i case ie.MSInfoChangeReportingAction: u.MSInfoChangeReportingAction = i case ie.EvolvedAllocationRetentionPriorityI: u.EvolvedARPI = i case ie.CSGInformationReportingAction: u.CSGInformationReportingAction = i case ie.AggregateMaximumBitRate: u.APNAMBR = i case ie.PrivateExtension: u.PrivateExtension = i default: u.AdditionalIEs = append(u.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (u *UpdatePDPContextResponse) MarshalLen() int { l := u.Header.MarshalLen() - len(u.Header.Payload) if ie := u.Cause; ie != nil { l += ie.MarshalLen() } if ie := u.Recovery; ie != nil { l += ie.MarshalLen() } if ie := u.TEIDDataI; ie != nil { l += ie.MarshalLen() } if ie := u.TEIDCPlane; ie != nil { l += ie.MarshalLen() } if ie := u.ChargingID; ie != nil { l += ie.MarshalLen() } if ie := u.PCO; ie != nil { l += ie.MarshalLen() } if ie := u.GGSNAddressForCPlane; ie != nil { l += ie.MarshalLen() } if ie := u.GGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := u.AltGGSNAddressForCPlane; ie != nil { l += ie.MarshalLen() } if ie := u.AltGGSNAddressForUserTraffic; ie != nil { l += ie.MarshalLen() } if ie := u.QoSProfile; ie != nil { l += ie.MarshalLen() } if ie := u.ChargingGatewayAddress; ie != nil { l += ie.MarshalLen() } if ie := u.AltChargingGatewayAddress; ie != nil { l += ie.MarshalLen() } if ie := u.CommonFlags; ie != nil { l += ie.MarshalLen() } if ie := u.APNRestriction; ie != nil { l += ie.MarshalLen() } if ie := u.BearerControlMode; ie != nil { l += ie.MarshalLen() } if ie := u.MSInfoChangeReportingAction; ie != nil { l += ie.MarshalLen() } if ie := u.EvolvedARPI; ie != nil { l += ie.MarshalLen() } if ie := u.CSGInformationReportingAction; ie != nil { l += ie.MarshalLen() } if ie := u.APNAMBR; ie != nil { l += ie.MarshalLen() } if ie := u.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range u.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (u *UpdatePDPContextResponse) SetLength() { u.Length = uint16(u.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (u *UpdatePDPContextResponse) MessageTypeName() string { return "Update PDP Context Response" } // TEID returns the TEID in human-readable string. func (u *UpdatePDPContextResponse) TEID() uint32 { return u.Header.TEID } ================================================ FILE: gtpv1/message/update-pdp-context-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes UpdatePDPContextResponse into bytes. // // Deprecated: use UpdatePDPContextResponse.Marshal instead. func (u *UpdatePDPContextResponse) Serialize() ([]byte, error) { log.Println("UpdatePDPContextResponse.Serialize is deprecated. use UpdatePDPContextResponse.Marshal instead") return u.Marshal() } // SerializeTo serializes UpdatePDPContextResponse into bytes given as b. // // Deprecated: use UpdatePDPContextResponse.MarshalTo instead. func (u *UpdatePDPContextResponse) SerializeTo(b []byte) error { log.Println("UpdatePDPContextResponse.SerializeTo is deprecated. use UpdatePDPContextResponse.MarshalTo instead") return u.MarshalTo(b) } // DecodeUpdatePDPContextResponse decodes bytes as UpdatePDPContextResponse. // // Deprecated: use ParseUpdatePDPContextResponse instead. func DecodeUpdatePDPContextResponse(b []byte) (*UpdatePDPContextResponse, error) { log.Println("DecodeUpdatePDPContextResponse is deprecated. use ParseUpdatePDPContextResponse instead") return ParseUpdatePDPContextResponse(b) } // DecodeFromBytes decodes bytes as UpdatePDPContextResponse. // // Deprecated: use UpdatePDPContextResponse.UnmarshalBinary instead. func (u *UpdatePDPContextResponse) DecodeFromBytes(b []byte) error { log.Println("UpdatePDPContextResponse.DecodeFromBytes is deprecated. use UpdatePDPContextResponse.UnmarshalBinary instead") return u.UnmarshalBinary(b) } // Len returns the actual length of UpdatePDPContextResponse. // // Deprecated: use UpdatePDPContextResponse.MarshalLen instead. func (u *UpdatePDPContextResponse) Len() int { log.Println("UpdatePDPContextResponse.Len is deprecated. use UpdatePDPContextResponse.MarshalLen instead") return u.MarshalLen() } ================================================ FILE: gtpv1/message/update-pdp-context-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestUpdatePDPContextResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewUpdatePDPContextResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv1.ResCauseRequestAccepted), ie.NewRecovery(0), ie.NewTEIDDataI(0xdeadbeef), ie.NewTEIDCPlane(0xdeadbeef), ie.NewGSNAddress("1.1.1.1"), ie.NewGSNAddress("2.2.2.2"), ), Serialized: []byte{ // Header 0x32, 0x13, 0x00, 0x20, 0x11, 0x22, 0x33, 0x44, 0x00, 0x01, 0x00, 0x00, // Cause 0x01, 0x80, // Recovery 0x0e, 0x00, // TEID-U 0x10, 0xde, 0xad, 0xbe, 0xef, // TEID-C 0x11, 0xde, 0xad, 0xbe, 0xef, // GSN Address 0x85, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, // GSN Address 0x85, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseUpdatePDPContextResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/message/version-not-supported.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv1/ie" ) // VersionNotSupported is a VersionNotSupported Header and its IEs above. type VersionNotSupported struct { *Header AdditionalIEs []*ie.IE } // NewVersionNotSupported creates a new GTPv1 VersionNotSupported. func NewVersionNotSupported(teid uint32, seq uint16, ie ...*ie.IE) *VersionNotSupported { v := &VersionNotSupported{ Header: NewHeader(0x32, MsgTypeVersionNotSupported, teid, seq, nil), } for _, i := range ie { if i == nil { continue } v.AdditionalIEs = append(v.AdditionalIEs, i) } v.SetLength() return v } // Marshal returns the byte sequence generated from a VersionNotSupported. func (v *VersionNotSupported) Marshal() ([]byte, error) { b := make([]byte, v.MarshalLen()) if err := v.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (v *VersionNotSupported) MarshalTo(b []byte) error { if len(b) < v.MarshalLen() { return ErrTooShortToMarshal } v.Header.Payload = make([]byte, v.MarshalLen()-v.Header.MarshalLen()) offset := 0 for _, ie := range v.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(v.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } v.Header.SetLength() return v.Header.MarshalTo(b) } // ParseVersionNotSupported decodes a given byte sequence as a VersionNotSupported. func ParseVersionNotSupported(b []byte) (*VersionNotSupported, error) { v := &VersionNotSupported{} if err := v.UnmarshalBinary(b); err != nil { return nil, err } return v, nil } // UnmarshalBinary decodes a given byte sequence as a VersionNotSupported. func (v *VersionNotSupported) UnmarshalBinary(b []byte) error { var err error v.Header, err = ParseHeader(b) if err != nil { return err } if len(v.Header.Payload) < 2 { return nil } ie, err := ie.ParseMultiIEs(v.Header.Payload) if err != nil { return err } for _, i := range ie { if i == nil { continue } v.AdditionalIEs = append(v.AdditionalIEs, i) } return nil } // MarshalLen returns the serial length of Data. func (v *VersionNotSupported) MarshalLen() int { l := v.Header.MarshalLen() - len(v.Header.Payload) for _, ie := range v.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (v *VersionNotSupported) SetLength() { v.Length = uint16(v.MarshalLen() - 8) } // MessageTypeName returns the name of protocol. func (v *VersionNotSupported) MessageTypeName() string { return "Version Not Supported" } // TEID returns the TEID in human-readable string. func (v *VersionNotSupported) TEID() uint32 { return v.Header.TEID } ================================================ FILE: gtpv1/message/version-not-supported_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes VersionNotSupported into bytes. // // Deprecated: use VersionNotSupported.Marshal instead. func (v *VersionNotSupported) Serialize() ([]byte, error) { log.Println("VersionNotSupported.Serialize is deprecated. use VersionNotSupported.Marshal instead") return v.Marshal() } // SerializeTo serializes VersionNotSupported into bytes given as b. // // Deprecated: use VersionNotSupported.MarshalTo instead. func (v *VersionNotSupported) SerializeTo(b []byte) error { log.Println("VersionNotSupported.SerializeTo is deprecated. use VersionNotSupported.MarshalTo instead") return v.MarshalTo(b) } // DecodeVersionNotSupported decodes bytes as VersionNotSupported. // // Deprecated: use ParseVersionNotSupported instead. func DecodeVersionNotSupported(b []byte) (*VersionNotSupported, error) { log.Println("DecodeVersionNotSupported is deprecated. use ParseVersionNotSupported instead") return ParseVersionNotSupported(b) } // DecodeFromBytes decodes bytes as VersionNotSupported. // // Deprecated: use VersionNotSupported.UnmarshalBinary instead. func (v *VersionNotSupported) DecodeFromBytes(b []byte) error { log.Println("VersionNotSupported.DecodeFromBytes is deprecated. use VersionNotSupported.UnmarshalBinary instead") return v.UnmarshalBinary(b) } // Len returns the actual length of VersionNotSupported. // // Deprecated: use VersionNotSupported.MarshalLen instead. func (v *VersionNotSupported) Len() int { log.Println("VersionNotSupported.Len is deprecated. use VersionNotSupported.MarshalLen instead") return v.MarshalLen() } ================================================ FILE: gtpv1/message/version-not-supported_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv1/testutils" ) func TestVersionNotSupported(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewVersionNotSupported( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ), Serialized: []byte{ // Header 0x32, 0x03, 0x00, 0x04, 0x11, 0x22, 0x33, 0x44, 0x00, 0x01, 0x00, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseVersionNotSupported(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv1/relay.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 import ( "log" "net" "sync" "time" ) // Relay is to relay packets between two UPlaneConn. // // Deprecated: Use UPlaneConn.RelayTo() instead. type Relay struct { mu sync.Mutex closeCh chan struct{} leftConn, rightConn *UPlaneConn teidPair map[uint32]*peer } // NewRelay creates a new Relay. // // Deprecated: Use UPlaneConn.RelayTo() instead. func NewRelay(leftConn, rightConn *UPlaneConn) *Relay { log.Println("Relay is deprecated. Use UPlaneConn.RelayTo() instead.") return &Relay{ mu: sync.Mutex{}, closeCh: make(chan struct{}), leftConn: leftConn, rightConn: rightConn, teidPair: map[uint32]*peer{}, } } // Run starts listening on both UPlaneConn. // Until peer information is registered by AddPeer(), it just drops packets. // // Deprecated: Use UPlaneConn.RelayTo() instead. func (r *Relay) Run() { // from left to right go func() { buf := make([]byte, 1600) for { select { case <-r.closed(): return default: // do nothing and go forward. } n, _, teid, err := r.leftConn.ReadFromGTP(buf) if err != nil { continue } peer, ok := r.getPeer(teid) if !ok { continue } if _, err := r.rightConn.WriteToGTP(peer.teid, buf[:n], peer.addr); err != nil { continue } } }() // from right to left go func() { buf := make([]byte, 1600) for { select { case <-r.closed(): return default: // do nothing and go forward. } n, _, teid, err := r.rightConn.ReadFromGTP(buf) if err != nil { continue } peer, ok := r.getPeer(teid) if !ok { continue } if _, err := r.leftConn.WriteToGTP(peer.teid, buf[:n], peer.addr); err != nil { continue } } }() } // Close closes Relay. It does not close the UPlaneConn given at first. // // Deprecated: Use UPlaneConn.RelayTo() instead. func (r *Relay) Close() error { if err := r.leftConn.SetReadDeadline(time.Now().Add(1 * time.Millisecond)); err != nil { return err } if err := r.rightConn.SetReadDeadline(time.Now().Add(1 * time.Millisecond)); err != nil { return err } close(r.closeCh) return nil } func (r *Relay) closed() <-chan struct{} { return r.closeCh } // AddPeer adds a peer information with the TEID contained in the incoming meesage. // // Deprecated: Use UPlaneConn.RelayTo() instead. func (r *Relay) AddPeer(teidIn, teidOut uint32, raddr net.Addr) { r.mu.Lock() defer r.mu.Unlock() r.teidPair[teidIn] = &peer{teid: teidOut, addr: raddr} } func (r *Relay) getPeer(teid uint32) (*peer, bool) { r.mu.Lock() defer r.mu.Unlock() p, ok := r.teidPair[teid] return p, ok } ================================================ FILE: gtpv1/relay_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1_test import ( "context" "net" "testing" "github.com/wmnsk/go-gtp/gtpv1" ) func TestRelay(t *testing.T) { leftAddr, err := net.ResolveUDPAddr("udp", "127.0.0.11:2152") if err != nil { t.Fatal(err) } rightAddr, err := net.ResolveUDPAddr("udp", "127.0.0.12:2152") if err != nil { t.Fatal(err) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() leftConn := gtpv1.NewUPlaneConn(leftAddr) go func() { if err := leftConn.ListenAndServe(ctx); err != nil { t.Errorf("failed to listen on %s: %s", leftConn.LocalAddr(), err) return } }() rightConn := gtpv1.NewUPlaneConn(rightAddr) go func() { if err := rightConn.ListenAndServe(ctx); err != nil { t.Errorf("failed to listen on %s: %s", rightConn.LocalAddr(), err) return } }() if err := leftConn.RelayTo(rightConn, 0x22222222, 0x11111111, rightAddr); err != nil { t.Fatal(err) } if err := rightConn.RelayTo(leftConn, 0x11111111, 0x22222222, leftAddr); err != nil { t.Fatal(err) } // TODO: add tests to check if the traffic goes through conns. } ================================================ FILE: gtpv1/testutils/testutils.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Package testutils is an internal package to be used for unit tests. Don't use this. package testutils import ( "testing" "github.com/pascaldekloe/goe/verify" "github.com/wmnsk/go-gtp/gtpv1/message" ) // Serializable is just for testing gtpv2.Messages. Don't use this. type Serializable interface { Marshal() ([]byte, error) MarshalLen() int } // TestCase is just for testing gtpv2.Messages. Don't use this. type TestCase struct { Description string Structured Serializable Serialized []byte } // ParseFunc is just for testing gtpv2.Messages. Don't use this. type ParseFunc func([]byte) (Serializable, error) // TestBearerInfo is just for testing gtpv2.Messages. Don't use this. var TestBearerInfo = struct { TEID uint32 Seq uint16 }{0x11223344, 0x00000001} // Run is just for testing gtpv2.Messages. Don't use this. func Run(t *testing.T, cases []TestCase, decode ParseFunc) { t.Helper() for _, c := range cases { t.Run(c.Description, func(t *testing.T) { t.Run("Parse", func(t *testing.T) { v, err := decode(c.Serialized) if err != nil { t.Fatal(err) } if got, want := v, c.Structured; !verify.Values(t, "", got, want) { t.Fail() } }) t.Run("Marshal", func(t *testing.T) { b, err := c.Structured.Marshal() if err != nil { t.Fatal(err) } if got, want := b, c.Serialized; !verify.Values(t, "", got, want) { t.Fail() } }) t.Run("Len", func(t *testing.T) { if got, want := c.Structured.MarshalLen(), len(c.Serialized); got != want { t.Fatalf("got %v want %v", got, want) } }) t.Run("Interface", func(t *testing.T) { // Ignore *Header and Generic in this tests. if _, ok := c.Structured.(*message.Header); ok { return } if _, ok := c.Structured.(*message.Generic); ok { return } decoded, err := message.Parse(c.Serialized) if err != nil { t.Fatal(err) } if got, want := decoded.Version(), c.Structured.(message.Message).Version(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := decoded.MessageType(), c.Structured.(message.Message).MessageType(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := decoded.MessageTypeName(), c.Structured.(message.Message).MessageTypeName(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := decoded.TEID(), c.Structured.(message.Message).TEID(); got != want { t.Fatalf("got %v want %v", got, want) } }) }) } } ================================================ FILE: gtpv1/tunnel.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 import ( "errors" "net" ) type peer struct { teid uint32 addr net.Addr srcConn *UPlaneConn } // RelayTo relays T-PDU type of packet to peer node(specified by raddr) from the UPlaneConn given. // // By using this, owner of UPlaneConn won't be able to Read and Write the packets that has teidIn. func (u *UPlaneConn) RelayTo(c *UPlaneConn, teidIn, teidOut uint32, raddr net.Addr) error { if u.KernelGTP.enabled { return errors.New("cannot call RelayTo when using Kernel GTP-U") } u.mu.Lock() defer u.mu.Unlock() if u.relayMap == nil { u.relayMap = map[uint32]*peer{} } u.relayMap[teidIn] = &peer{teid: teidOut, addr: raddr, srcConn: c} return nil } // CloseRelay stops relaying T-PDU from a conn to conn. func (u *UPlaneConn) CloseRelay(teidIn uint32) error { if u.KernelGTP.enabled { return errors.New("cannot call CloseRelay when using Kernel GTP-U") } u.mu.Lock() delete(u.relayMap, teidIn) u.mu.Unlock() u.iteiMap.delete(teidIn) return nil } ================================================ FILE: gtpv1/tunnel_linux.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 import ( "errors" "fmt" "net" "github.com/vishvananda/netlink" ) // Role is a role for Kernel GTP-U. type Role int // Role definitions. const ( RoleGGSN Role = iota RoleSGSN ) // EnableKernelGTP enables Linux Kernel GTP-U. // Note that this removes all the existing userland tunnels, and cannot be disabled while // the program is working (at least at this moment). // // Using Kernel GTP-U is much more performant than userland, but requires root privilege. // After enabled, users should add tunnels by AddTunnel func, and also add appropriate // routing entries. For handling downlink traffic on P-GW, for example; // // ip route add dev table // ip rule add from lookup
// // This let the traffic from SGi side of network I/F to be forwarded to GTP device, // and if the UE's IP is known to Kernel GTP-U(by AddTunnel), it is encapsulated and // forwarded to the peer(S-GW, in this case). // // Please see the examples/gw-tester for how each node handles routing from the program. func (u *UPlaneConn) EnableKernelGTP(devname string, role Role) error { if u.pktConn == nil { var err error u.pktConn, err = newPktConn(u.laddr) if err != nil { return err } } f, err := u.pktConn.File() if err != nil { return fmt.Errorf("failed to retrieve file from conn: %w", err) } u.KernelGTP.Link = &netlink.GTP{ LinkAttrs: netlink.LinkAttrs{ Name: devname, }, FD1: int(f.Fd()), Role: int(role), } if err := netlink.LinkAdd(u.KernelGTP.Link); err != nil { _ = f.Close() return fmt.Errorf("failed to add device %s: %w", u.KernelGTP.Link.Name, err) } if err := netlink.LinkSetUp(u.KernelGTP.Link); err != nil { _ = f.Close() return fmt.Errorf("failed to setup device %s: %w", u.KernelGTP.Link.Name, err) } if err := netlink.LinkSetMTU(u.KernelGTP.Link, 1500); err != nil { _ = f.Close() return fmt.Errorf("failed to set MTU for device %s: %w", u.KernelGTP.Link.Name, err) } u.KernelGTP.connFile = f u.KernelGTP.enabled = true // remove relayed userland tunnels if exists if len(u.relayMap) != 0 { u.mu.Lock() u.relayMap = nil u.mu.Unlock() } return nil } // AddTunnel adds a GTP-U tunnel with Linux Kernel GTP-U via netlink. func (u *UPlaneConn) AddTunnel(peerIP, msIP net.IP, otei, itei uint32) error { if !u.KernelGTP.enabled { return errors.New("cannot call AddTunnel when not using Kernel GTP-U") } pdp := &netlink.PDP{ Version: 1, PeerAddress: peerIP, MSAddress: msIP, OTEI: otei, ITEI: itei, } if err := netlink.GTPPDPAdd(u.KernelGTP.Link, pdp); err != nil { return fmt.Errorf("failed to add tunnel for %s with %s: %w", msIP, peerIP, err) } return nil } // AddTunnelOverride adds a GTP-U tunnel with Linux Kernel GTP-U via netlink. // If there is already an existing tunnel that has the same msIP and/or incoming TEID, // this deletes it before adding the tunnel. func (u *UPlaneConn) AddTunnelOverride(peerIP, msIP net.IP, otei, itei uint32) error { if !u.KernelGTP.enabled { return errors.New("cannot call AddTunnelOverride when not using Kernel GTP-U") } if pdp, _ := netlink.GTPPDPByMSAddress(u.KernelGTP.Link, msIP); pdp != nil { // do nothing even this fails _ = netlink.GTPPDPDel(u.KernelGTP.Link, pdp) } if pdp, _ := netlink.GTPPDPByITEI(u.KernelGTP.Link, int(itei)); pdp != nil { // do nothing even this fails _ = netlink.GTPPDPDel(u.KernelGTP.Link, pdp) } return u.AddTunnel(peerIP, msIP, otei, itei) } // DelTunnelByITEI deletes a Linux Kernel GTP-U tunnel specified with the incoming TEID. func (u *UPlaneConn) DelTunnelByITEI(itei uint32) error { if !u.KernelGTP.enabled { return errors.New("cannot call DelTunnel when not using Kernel GTP-U") } pdp, err := netlink.GTPPDPByITEI(u.KernelGTP.Link, int(itei)) if err != nil { return fmt.Errorf("failed to delete tunnel with %d: %w", itei, err) } if err := netlink.GTPPDPDel(u.KernelGTP.Link, pdp); err != nil { return fmt.Errorf("failed to delete tunnel for %s: %w", pdp, err) } u.iteiMap.delete(itei) return nil } // DelTunnelByMSAddress deletes a Linux Kernel GTP-U tunnel specified with the subscriber's IP. func (u *UPlaneConn) DelTunnelByMSAddress(msIP net.IP) error { if !u.KernelGTP.enabled { return errors.New("cannot call DelTunnel when not using Kernel GTP-U") } pdp, err := netlink.GTPPDPByMSAddress(u.KernelGTP.Link, msIP) if err != nil { return fmt.Errorf("failed to delete tunnel with %s: %w", msIP, err) } itei := pdp.ITEI if err := netlink.GTPPDPDel(u.KernelGTP.Link, pdp); err != nil { return fmt.Errorf("failed to delete tunnel for %s: %w", pdp, err) } u.iteiMap.delete(itei) return nil } ================================================ FILE: gtpv1/u-conn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 import ( "context" "crypto/rand" "encoding/binary" "errors" "fmt" "io" "net" "os" "strings" "sync" "time" "github.com/vishvananda/netlink" "github.com/wmnsk/go-gtp/gtpv1/ie" "github.com/wmnsk/go-gtp/gtpv1/message" v2ie "github.com/wmnsk/go-gtp/gtpv2/ie" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" ) type tpduSet struct { raddr net.Addr teid uint32 seq uint16 payload []byte } type pktConn interface { // WriteToWithDSCPECN writes a packet with payload p to addr using the given DSCP/ECN value. // WriteToWithDSCPECN can be made to time out and return // an Error with Timeout() == true after a fixed time limit; // see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. WriteToWithDSCPECN(p []byte, addr net.Addr, dscpecn int) (n int, err error) // File returns a copy of the underlying os.File. It is the caller's responsibility to close f when finished. // Closing c does not affect f, and closing f does not affect c. // The returned os.File's file descriptor is different from the connection's. // Attempting to change properties of the original using this duplicate may or may not have the desired effect. File() (f *os.File, err error) net.PacketConn } type pktConn4 struct { // mu is the mutex used before Writing to the PacketConn, // to be sure the right DSCP/ECN value // is applied before performing the Write. mu *sync.Mutex // udpConn is the UDPConn used as underlying transport udpConn *net.UDPConn *ipv4.PacketConn } // ReadFrom implements the io.ReaderFrom ReadFrom method. func (pkt pktConn4) ReadFrom(b []byte) (n int, src net.Addr, err error) { n, _, src, err = pkt.PacketConn.ReadFrom(b) return n, src, err } // WriteTo implements the PacketConn WriteTo method. func (pkt pktConn4) WriteTo(b []byte, dst net.Addr) (n int, err error) { return pkt.PacketConn.WriteTo(b, nil, dst) } // setDSCPECN sets the DSCP/ECN value used for next writes. func (pkt pktConn4) setDSCPECN(dscpecs int) error { // With IPv4, DSCP/ECN is using the TOS field return pkt.SetTOS(dscpecs) } // DSCPECN returns the DSCP/ECN value. func (pkt pktConn4) DSCPECN() (int, error) { // With IPv4, DSCP/ECN is using the TOS field return pkt.TOS() } // WriteToWithDSCPECN implements the pktConn WriteToWithDSCPECN method. func (pkt pktConn4) WriteToWithDSCPECN(p []byte, addr net.Addr, dscpecn int) (n int, err error) { pkt.mu.Lock() defer pkt.mu.Unlock() oldDSCPECN, err := pkt.DSCPECN() if err != nil { return 0, err } err = pkt.setDSCPECN(dscpecn) if err != nil { return 0, err } defer func() { // set back DSCP/ECN for next write calls _ = pkt.setDSCPECN(oldDSCPECN) }() return pkt.WriteTo(p, addr) } // File returns a copy of the underlying os.File. It is the caller's responsibility to close f when finished. // Closing c does not affect f, and closing f does not affect c. // The returned os.File's file descriptor is different from the connection's. // Attempting to change properties of the original using this duplicate may or may not have the desired effect. func (pkt pktConn4) File() (f *os.File, err error) { return pkt.udpConn.File() } type pktConn6 struct { // mu is the mutex used before Writing to the PacketConn, // to be sure the right DSCP/ECN value // is applied before performing the Write. mu *sync.Mutex // udpConn is the UDPConn used as underlying transport. udpConn *net.UDPConn *ipv6.PacketConn } // ReadFrom implements the io.ReaderFrom ReadFrom method. func (pkt pktConn6) ReadFrom(b []byte) (n int, src net.Addr, err error) { n, _, src, err = pkt.PacketConn.ReadFrom(b) return n, src, err } // WriteTo implements the PacketConn WriteTo method. func (pkt pktConn6) WriteTo(b []byte, dst net.Addr) (n int, err error) { return pkt.PacketConn.WriteTo(b, nil, dst) } // setDSCPECN sets the DSCP/ECN value used for next writes. func (pkt pktConn6) setDSCPECN(dscpecs int) error { // With IPv6, DSCP/ECN is using the Traffic Class field return pkt.SetTrafficClass(dscpecs) } // DSCPECN returns the DSCP/ECN value. func (pkt pktConn6) DSCPECN() (int, error) { // With IPv6, DSCP/ECN is using the Traffic Class field return pkt.TrafficClass() } // WriteToWithDSCPECN implements the pktConn WriteToWithDSCPECN method. func (pkt pktConn6) WriteToWithDSCPECN(p []byte, addr net.Addr, dscpecn int) (n int, err error) { pkt.mu.Lock() defer pkt.mu.Unlock() oldDSCPECN, err := pkt.DSCPECN() if err != nil { return 0, err } err = pkt.setDSCPECN(dscpecn) if err != nil { return 0, err } defer func() { // set back DSCP/ECN for next write calls _ = pkt.setDSCPECN(oldDSCPECN) }() return pkt.WriteTo(p, addr) } // File returns a copy of the underlying os.File. It is the caller's responsibility to close f when finished. // Closing c does not affect f, and closing f does not affect c. // The returned os.File's file descriptor is different from the connection's. // Attempting to change properties of the original using this duplicate may or may not have the desired effect. func (pkt pktConn6) File() (f *os.File, err error) { return pkt.udpConn.File() } // newPktConn creates a new pktConn initialized with a given local UDP address func newPktConn(laddr net.Addr) (pktConn, error) { var err error pktC, err := net.ListenPacket(laddr.Network(), laddr.String()) if err != nil { return nil, err } // check if UDPConn is over IPv4 or IPv6 addr, err := net.ResolveUDPAddr(laddr.Network(), laddr.String()) if err != nil { return nil, err } if addr.IP.To4() != nil { return pktConn4{ mu: &sync.Mutex{}, udpConn: pktC.(*net.UDPConn), PacketConn: ipv4.NewPacketConn(pktC), }, nil } else if addr.IP.To16() != nil { return pktConn6{ mu: &sync.Mutex{}, udpConn: pktC.(*net.UDPConn), PacketConn: ipv6.NewPacketConn(pktC), }, nil } return nil, fmt.Errorf("laddr must refer to an IP address") } // UPlaneConn represents a U-Plane Connection of GTPv1. type UPlaneConn struct { mu sync.Mutex laddr net.Addr pktConn pktConn *msgHandlerMap *iteiMap tpduCh chan *tpduSet closeCh chan struct{} relayMap map[uint32]*peer errIndEnabled bool // for Linux kernel GTP with netlink KernelGTP } // KernelGTP consists of the Linux Kernel GTP-U related objects. type KernelGTP struct { enabled bool connFile *os.File Link *netlink.GTP } // NewUPlaneConn creates a new UPlaneConn used for server. On client side, use DialUPlane instead. func NewUPlaneConn(laddr net.Addr) *UPlaneConn { return &UPlaneConn{ mu: sync.Mutex{}, msgHandlerMap: newDefaultMsgHandlerMap(), iteiMap: newiteiMap(), laddr: laddr, tpduCh: make(chan *tpduSet), closeCh: make(chan struct{}), errIndEnabled: true, } } // DialUPlane sends Echo Request to raddr to check if the endpoint is alive and returns UPlaneConn. func DialUPlane(ctx context.Context, laddr, raddr net.Addr) (*UPlaneConn, error) { u := &UPlaneConn{ mu: sync.Mutex{}, msgHandlerMap: newDefaultMsgHandlerMap(), iteiMap: newiteiMap(), laddr: laddr, tpduCh: make(chan *tpduSet), closeCh: make(chan struct{}), errIndEnabled: true, } // setup UDPConn first. var err error if u.pktConn == nil { u.pktConn, err = newPktConn(u.laddr) if err != nil { return nil, err } } // if no response coming within 5 seconds, returns error. if err := u.SetReadDeadline(time.Now().Add(5 * time.Second)); err != nil { return nil, err } buf := make([]byte, 1600) for { select { case <-ctx.Done(): return nil, nil default: // go forward } // send EchoRequest to raddr. if err := u.EchoRequest(raddr); err != nil { return nil, err } n, _, err := u.ReadFrom(buf) if err != nil { return nil, err } if err := u.SetReadDeadline(time.Time{}); err != nil { return nil, err } // decode incoming message and let it be handled by default handler funcs. msg, err := message.Parse(buf[:n]) if err != nil { return nil, err } if _, ok := msg.(*message.EchoResponse); !ok { continue } break } go func() { if err := u.serve(ctx); err != nil { logf("fatal error on UPlaneConn %s: %s", u.LocalAddr(), err) } }() return u, nil } // ListenAndServe creates a new GTPv2-C *Conn and start serving. // This blocks, and returns error only if it face the fatal one. Non-fatal errors are logged // with logger. See SetLogger/EnableLogger/DisableLogger for handling of those logs. func (u *UPlaneConn) ListenAndServe(ctx context.Context) error { if u.pktConn == nil { var err error u.mu.Lock() u.pktConn, err = newPktConn(u.laddr) u.mu.Unlock() if err != nil { return err } } return u.listenAndServe(ctx) } func (u *UPlaneConn) listenAndServe(ctx context.Context) error { // TODO: this func is left for future enhancement. return u.serve(ctx) } func (u *UPlaneConn) serve(ctx context.Context) error { go func() { select { // ctx is canceled or Close() is called case <-ctx.Done(): case <-u.closed(): } if u.KernelGTP.enabled { if err := u.KernelGTP.connFile.Close(); err != nil { logf("error closing GTPFile: %s", err) } if err := netlink.LinkDel(u.KernelGTP.Link); err != nil { logf("error deleting GTPLink: %s", err) } } // This doesn't finish for some reason when Kernel GTP is enabled. if u.pktConn != nil { if err := u.pktConn.Close(); err != nil { logf("error closing the underlying conn: %s", err) } } }() buf := make([]byte, 1500) for { select { case <-ctx.Done(): return nil case <-u.closed(): return nil default: // do nothing and go forward. } n, raddr, err := u.ReadFrom(buf) if err != nil { if errors.Is(err, io.EOF) { return nil } // TODO: Use net.ErrClosed instead (available from Go 1.16). // https://github.com/golang/go/commit/e9ad52e46dee4b4f9c73ff44f44e1e234815800f if strings.Contains(err.Error(), "use of closed network connection") { return nil } return fmt.Errorf("error reading from UPlaneConn %s: %w", u.LocalAddr(), err) } raw := make([]byte, n) copy(raw, buf) go func() { // just forward T-PDU instead of passing it to reader if relayer is // configured and the message type is T-PDU. if len(u.relayMap) != 0 && raw[1] == message.MsgTypeTPDU { // ignore if the packet size is smaller than minimum header size if n < 11 { return } u.mu.Lock() peer, ok := u.relayMap[binary.BigEndian.Uint32(raw[4:8])] u.mu.Unlock() if !ok { // pass message to handler if TEID is unknown msg, err := message.Parse(raw[:n]) if err != nil { return } if err := u.handleMessage(raddr, msg); err != nil { // should not stop serving with this error logf("error handling message on UPlaneConn %s: %v", u.LocalAddr(), err) } return } // just use original packet not to get it slow. binary.BigEndian.PutUint32(raw[4:8], peer.teid) if _, err := peer.srcConn.WriteToWithDSCPECN(raw[:n], peer.addr, 0); err != nil { // should not stop serving with this error logf("error sending on UPlaneConn %s: %v", u.LocalAddr(), err) } return } msg, err := message.Parse(raw[:n]) if err != nil { logf("error parsing message on UPlaneConn %s: %v", u.LocalAddr(), err) return } if err := u.handleMessage(raddr, msg); err != nil { // should not stop serving with this error logf("error handling message on UPlaneConn %s: %v", u.LocalAddr(), err) return } }() } } // ReadFrom reads a packet from the connection, // copying the payload into p. It returns the number of // bytes copied into p and the return address that // was on the packet. // It returns the number of bytes read (0 <= n <= len(p)) // and any error encountered. Callers should always process // the n > 0 bytes returned before considering the error err. // ReadFrom can be made to time out and return // an Error with Timeout() == true after a fixed time limit; // see SetDeadline and SetReadDeadline. // // Note that valid GTP-U packets handled by Kernel can NOT be retrieved by this. func (u *UPlaneConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { return u.pktConn.ReadFrom(p) } // ReadFromGTP reads a packet from the connection, copying the payload without // GTP header into p. It returns the number of bytes copied into p, the return // address that was on the packet, TEID in the GTP header. // // Note that valid GTP-U packets handled by Kernel can NOT be retrieved by this. func (u *UPlaneConn) ReadFromGTP(p []byte) (n int, addr net.Addr, teid uint32, err error) { select { case <-u.closed(): return case tpdu, ok := <-u.tpduCh: if !ok { err = ErrConnNotOpened return } n = copy(p, tpdu.payload) addr = tpdu.raddr teid = tpdu.teid return } } // WriteTo writes a packet with payload p to addr. // WriteTo can be made to time out and return // an Error with Timeout() == true after a fixed time limit; // see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. func (u *UPlaneConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { u.mu.Lock() defer u.mu.Unlock() return u.pktConn.WriteToWithDSCPECN(p, addr, 0) } // WriteToWithDSCPECN writes a packet with payload p to addr using the given DSCP/ECN value. // WriteToWithDSCPECN can be made to time out and return // an Error with Timeout() == true after a fixed time limit; // see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. func (u *UPlaneConn) WriteToWithDSCPECN(p []byte, addr net.Addr, dscpecn int) (n int, err error) { return u.pktConn.WriteToWithDSCPECN(p, addr, dscpecn) } // WriteToGTP writes a packet with TEID and payload to addr. func (u *UPlaneConn) WriteToGTP(teid uint32, p []byte, addr net.Addr) (n int, err error) { b, err := Encapsulate(teid, p).Marshal() if err != nil { return } if _, err = u.WriteTo(b, addr); err != nil { return } return len(b), nil } // closed would be used in multiple goroutines. // never send struct{}{} to it; instead, use close(u.closeCh). func (u *UPlaneConn) closed() <-chan struct{} { return u.closeCh } // Close closes the connection. // Any blocked Read or Write operations will be unblocked and return errors. func (u *UPlaneConn) Close() error { u.mu.Lock() defer u.mu.Unlock() close(u.closeCh) return nil } // LocalAddr returns the local network address. func (u *UPlaneConn) LocalAddr() net.Addr { return u.pktConn.LocalAddr() } // SetDeadline sets the read and write deadlines associated // with the connection. It is equivalent to calling both // SetReadDeadline and SetWriteDeadline. // // A deadline is an absolute time after which I/O operations // fail with a timeout (see type Error) instead of // blocking. The deadline applies to all future and pending // I/O, not just the immediately following call to Read or // Write. After a deadline has been exceeded, the connection // can be refreshed by setting a deadline in the future. // // An idle timeout can be implemented by repeatedly extending // the deadline after successful Read or Write calls. // // A zero value for t means I/O operations will not time out. func (u *UPlaneConn) SetDeadline(t time.Time) error { return u.pktConn.SetDeadline(t) } // SetReadDeadline sets the deadline for future Read calls // and any currently-blocked Read call. // A zero value for t means Read will not time out. func (u *UPlaneConn) SetReadDeadline(t time.Time) error { return u.pktConn.SetReadDeadline(t) } // SetWriteDeadline sets the deadline for future Write calls // and any currently-blocked Write call. // Even if write times out, it may return n > 0, indicating that // some of the data was successfully written. // A zero value for t means Write will not time out. func (u *UPlaneConn) SetWriteDeadline(t time.Time) error { return u.pktConn.SetWriteDeadline(t) } // AddHandler adds a message handler to *UPlaneConn. // // By adding HandlerFuncs, *UPlaneConn (and *Session, *Bearer created by the *UPlaneConn) will handle // the specified type of message with it's paired HandlerFunc when receiving. // Messages without registered handlers are just ignored and discarded and the user will // get ErrNoHandlersFound error. // // This should be performed just after creating *UPlaneConn, otherwise the user cannot retrieve // any values, which is in most cases vital to continue working as a node, from the incoming // message. // // HandlerFuncs for EchoResponse and ErrorIndication are registered by default. // These HandlerFuncs can be overwritten by specifying message.MsgTypeEchoResponse and/or // message.MsgTypeErrorIndication as msgType parameter. func (u *UPlaneConn) AddHandler(msgType uint8, fn HandlerFunc) { u.msgHandlerMap.store(msgType, fn) } // AddHandlers adds multiple handler funcs at a time. // // See AddHandler for detailed usage. func (u *UPlaneConn) AddHandlers(funcs map[uint8]HandlerFunc) { for msgType, fn := range funcs { u.msgHandlerMap.store(msgType, fn) } } func (u *UPlaneConn) handleMessage(senderAddr net.Addr, msg message.Message) error { handle, ok := u.msgHandlerMap.load(msg.MessageType()) if !ok { return &HandlerNotFoundError{MsgType: msg.MessageTypeName()} } if err := handle(u, senderAddr, msg); err != nil { return fmt.Errorf("failed to handle %s: %w", msg.MessageTypeName(), err) } return nil } // EchoRequest sends a EchoRequest. func (u *UPlaneConn) EchoRequest(raddr net.Addr) error { b, err := message.NewEchoRequest(0).Marshal() if err != nil { return err } if _, err := u.WriteTo(b, raddr); err != nil { return err } return nil } // EchoResponse sends a EchoResponse. func (u *UPlaneConn) EchoResponse(raddr net.Addr) error { b, err := message.NewEchoResponse(0, ie.NewRecovery(0)).Marshal() if err != nil { return err } if _, err := u.WriteTo(b, raddr); err != nil { return err } return nil } // ErrorIndication just sends ErrorIndication message. func (u *UPlaneConn) ErrorIndication(raddr net.Addr, received message.Message) error { ip, _, err := net.SplitHostPort(u.LocalAddr().String()) if err != nil { return err } errInd, err := message.NewErrorIndication( 0, received.Sequence(), ie.NewTEIDDataI(received.TEID()), ie.NewGSNAddress(ip), ).Marshal() if err != nil { return err } if _, err := u.WriteTo(errInd, raddr); err != nil { return err } return nil } // RespondTo sends a message(specified with "toBeSent" param) in response to // a message(specified with "received" param). // // This is to make it easier to handle SequenceNumber. func (u *UPlaneConn) RespondTo(raddr net.Addr, received, toBeSent message.Message) error { toBeSent.SetSequenceNumber(received.Sequence()) b := make([]byte, toBeSent.MarshalLen()) if err := toBeSent.MarshalTo(b); err != nil { return err } if _, err := u.WriteTo(b, raddr); err != nil { return err } return nil } // Restarts returns the number of restarts in uint8. func (u *UPlaneConn) Restarts() uint8 { return 0 } // NewFTEID creates a new GTPv2 F-TEID with random TEID value that is unique within UPlaneConn. // To ensure the uniqueness, don't create in the other way if you once use this method. // This is meant to be used for creating F-TEID IE for non-local interface type, such as // the ones that are used in U-Plane. For local interface, use (*Conn).NewSenderFTEID instead. // // Note that in the case there's a lot of Session on the Conn, it may take a long // time to find a new unique value. // // TODO: optimize performance... func (u *UPlaneConn) NewFTEID(ifType uint8, v4, v6 string) (fteidIE *v2ie.IE) { var teid uint32 for try := uint32(0); try < 0xffff; try++ { const logEvery = 0xff if try&logEvery == logEvery { logf("Generating NewSenderFTEID crossed tries:%d", try) } t := generateRandomUint32() if t == 0 { continue } // Try to mark TEID as taken. Fails if something exists if ok := u.iteiMap.tryStore(t, time.Now()); !ok { logf("TEID-U: %#08x has already been taken, trying to generate another one...", t) continue } teid = t break } if teid == 0 { return nil } return v2ie.NewFullyQualifiedTEID(ifType, teid, v4, v6) } func generateRandomUint32() uint32 { b := make([]byte, 4) if _, err := rand.Read(b); err != nil { return 0 } return binary.BigEndian.Uint32(b) } type iteiMap struct { syncMap sync.Map } func newiteiMap() *iteiMap { return &iteiMap{} } func (t *iteiMap) tryStore(itei uint32, ts time.Time) bool { _, loaded := t.syncMap.LoadOrStore(itei, ts) return !loaded } func (t *iteiMap) delete(itei uint32) { t.syncMap.Delete(itei) } // EnableErrorIndication re-enables automatic sending of // Error Indication to unknown messages, which is enabled by // default. // // See also: DisableErrorIndication. func (u *UPlaneConn) EnableErrorIndication() { u.mu.Lock() u.errIndEnabled = true u.mu.Unlock() } // DisableErrorIndication makes default T-PDU handler stop // responding with Error Indication in case of receiving T-PDU // with unknown TEID. // // When disabled, it passes the unhandled T-PDU to user who calls // ReadFromGTP instead. func (u *UPlaneConn) DisableErrorIndication() { u.mu.Lock() u.errIndEnabled = false u.mu.Unlock() } ================================================ FILE: gtpv1/u-conn_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1_test import ( "context" "net" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/wmnsk/go-gtp/gtpv1" ) type testVal struct { teidIn, teidOut uint32 seq uint16 payload []byte } func setup(ctx context.Context) (cliConn, srvConn *gtpv1.UPlaneConn, err error) { cliAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:2152") if err != nil { return nil, nil, err } srvAddr, err := net.ResolveUDPAddr("udp", "127.0.0.2:2152") if err != nil { return nil, nil, err } go func() { srvConn = gtpv1.NewUPlaneConn(srvAddr) srvConn.DisableErrorIndication() if err := srvConn.ListenAndServe(ctx); err != nil { return } }() // XXX - waiting for server to be well-prepared, should consider better way. time.Sleep(1 * time.Second) cliConn, err = gtpv1.DialUPlane(ctx, cliAddr, srvAddr) if err != nil { return nil, nil, err } cliConn.DisableErrorIndication() return cliConn, srvConn, nil } func TestClientWrite(t *testing.T) { var ( okCh = make(chan struct{}) errCh = make(chan error) buf = make([]byte, 2048) tv = &testVal{ 0x11111111, 0x22222222, 0x3333, []byte{0xde, 0xad, 0xbe, 0xef}, } ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() cliConn, srvConn, err := setup(ctx) if err != nil { t.Fatal(err) } go func(tv *testVal) { n, addr, teid, err := srvConn.ReadFromGTP(buf) if err != nil { errCh <- err return } if diff := cmp.Diff(n, len(tv.payload)); diff != "" { t.Error(diff) } if diff := cmp.Diff(addr, cliConn.LocalAddr()); diff != "" { t.Error(diff) } if diff := cmp.Diff(teid, tv.teidOut); diff != "" { t.Error(diff) } if diff := cmp.Diff(buf[:n], tv.payload); diff != "" { t.Error(diff) } okCh <- struct{}{} }(tv) if _, err := cliConn.WriteToGTP(tv.teidOut, tv.payload, srvConn.LocalAddr()); err != nil { t.Fatal(err) } select { case <-okCh: return case err := <-errCh: t.Fatal(err) case <-time.After(10 * time.Second): t.Fatal("timed out while waiting for response to come") } } ================================================ FILE: gtpv1/utils.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv1 import "github.com/wmnsk/go-gtp/gtpv1/message" // Encapsulate encapsulates given payload with GTPv1-U Header and returns message.TPDU. func Encapsulate(teid uint32, payload []byte) *message.TPDU { pdu := message.NewTPDU(teid, payload) return pdu } // EncapsulateWithExtensionHeader encapsulates given payload and Extension Headers // with GTPv1-U Header and returns message.TPDU. func EncapsulateWithExtensionHeader(teid uint32, payload []byte, extHdrs ...*message.ExtensionHeader) *message.TPDU { pdu := message.NewTPDUWithExtentionHeader(teid, payload, extHdrs...) return pdu } // Decapsulate decapsulates given bytes and returns TEID and Payload. func Decapsulate(b []byte) (uint32, []byte, error) { header, err := message.ParseHeader(b) if err != nil { return 0, nil, err } if header.Type != message.MsgTypeTPDU { return 0, nil, message.ErrInvalidMessageType } return header.TEID, header.Payload, nil } // DecapsulateWithExtensionHeader decapsulates given bytes and returns TEID, // Payload, and Extension Headers. // It is always safe to use this even if there may be no Extension Headers. func DecapsulateWithExtensionHeader(b []byte) (uint32, []byte, []*message.ExtensionHeader, error) { h, err := message.ParseHeader(b) if err != nil { return 0, nil, nil, err } if h.Type != message.MsgTypeTPDU { return 0, nil, nil, message.ErrInvalidMessageType } return h.TEID, h.Payload, h.ExtensionHeaders, nil } ================================================ FILE: gtpv2/README.md ================================================ # gtpv2: GTPv2 in Golang Package v2 provides simple and painless handling of GTPv2-C protocol in pure Golang. ## Getting Started _Working examples are available in [example](../examples) directory, which might be the better instruction for developers._ ### Opening a connection #### Client `Dial` opens a connection between the specified peer by confirming the peer is alive by Echo Request/Response exchange. If you don't need Echo, see Server section. ```go // Note that the conn is not bound to raddr. to let a Conn to be able to communicate with multiple peers. // Interface type is required here to let Conn know which is the local interface type. conn, err := gtpv2.Dial(ctx, laddr, raddr, gtpv2.IFTypeS11MMEGTPC, 0) if err != nil { // ... } ``` #### Server Retrieve `Conn` with `NewConn`, and `ListenAndServe` to start listening. ```go // Interface type is required here to let Conn know which is the local interface type. srvConn := gtpv2.NewConn(srvAddr, gtpv2.IFTypeS11MMEGTPC, 0) if err := srvConn.ListenAndServe(ctx); err != nil { // ... } ``` ### Handling incoming messages Prepare functions that comform to [`HandlerFunc`](https://pkg.go.dev/github.com/wmnsk/go-gtp/gtpv2#Conn.AddHandler), and register them to `Conn` with `AddHandler`. This should be done as soon as you get `Conn` not to miss the incoming messages. `HandlerFunc` is to handle the incoming messages by message type. See [example](../examples) for how it is like. Also consider using `AddHandlers` when you have many `HandlerFunc`s. ```go // write what you expect to do on receiving a message. Handlers should be added per message type. // by default, Echo Request/Response and Version Not Supported Indication is handled automatically. conn.AddHandler( // first param is the type of message. give number in uint8 or use gtpgtpv2.MsgTypeXXX. messages.MsgTypeCreateSessionResponse, // second param is the HandlerFunc to describe how you handle the message coming from peer. func(c *gtpv2.Conn, senderAddr net.Addr, msg messages.Message) error { // Do what you want with CreateSessionResponse message here. // see examples directly for functional examples }, ) ``` ### Manipulating sessions With `Conn`, you can create, modify, delete GTPv2-C sessions and bearers with the built-in methods. #### Session creation `CreateSession` creates a Session, while storing values given as IEs and creating a default bearer. In the following call, for example, IMSI and TEID for S1-U eNB is stored in the created `Session`. ```go session, err := c.CreateSession( // put IEs required for your implementation here. // it is easier to use constructors in ie package. ie.NewIMSI("123451234567890"), // or, you can use ie.New() to create an IE without type-specific constructor. // put the type of IE, flags/instance, and payload as the parameters. ie.New(ie.ExtendedTraceInformation, 0x00, []byte{0xde, 0xad, 0xbe, 0xef}), // to set the instance to IE created with message-specific constructor, WithInstance() // may be your help. ie.NewIMSI("123451234567890").WithInstance(1), // no one wants to set instance to IMSI, though. // don't forget to contain the Sender F-TEID, as it is used to distinguish the incoming // message by Conn. // // to be secure, TEID should be generated with random values, without conflicts in a Conn. // to achieve that, gtpv2 provides NewFTEID() which returns F-TEID in *ie.IE. s11Conn.NewFTEID(gtpv2.IFTypeS11MMEGTPC, mmeIP, ""), ) if err != nil { // ... } ``` #### Session deletion / Bearer modification `DeleteSession` and `ModifyBearer` methods are provided to send each message as easy as possible. Unlike `CreateSession`, they don't manipulate the Session information automatically. ### Opening a U-Plane connection _See [v1/README.md](../gtpv1/README.md#opening-a-u-plane-connection)._ ## Supported Features The following Messages marked with "Yes" are currently available with their own useful constructors. _Even there are some missing Messages, you can create any kind of Message by using `messages.NewGeneric()`._ ### Messages | ID | Name | Supported | |---------|-------------------------------------------------|-----------| | 0 | (Spare/Reserved) | - | | 1 | Echo Request | Yes | | 2 | Echo Response | Yes | | 3 | Version Not Supported Indication | Yes | | 4-16 | (Spare/Reserved) | - | | 17-24 | (Spare/Reserved) | - | | 25-31 | (Spare/Reserved) | - | | 32 | Create Session Request | Yes | | 33 | Create Session Response | Yes | | 34 | Modify Bearer Request | Yes | | 35 | Modify Bearer Response | Yes | | 36 | Delete Session Request | Yes | | 37 | Delete Session Response | Yes | | 38 | Change Notification Request | | | 39 | Change Notification Response | | | 40 | Remote UE Report Notification | | | 41 | Remote UE Report Acknowledge | | | 42-63 | (Spare/Reserved) | - | | 64 | Modify Bearer Command | Yes | | 65 | Modify Bearer Failure Indication | Yes | | 66 | Delete Bearer Command | Yes | | 67 | Delete Bearer Failure Indication | Yes | | 68 | Bearer Resource Command | | | 69 | Bearer Resource Failure Indication | | | 70 | Downlink Data Notification Failure Indication | Yes | | 71 | Trace Session Activation | | | 72 | Trace Session Deactivation | | | 73 | Stop Paging Indication | Yes | | 74-94 | (Spare/Reserved) | - | | 95 | Create Bearer Request | Yes | | 96 | Create Bearer Response | Yes | | 97 | Update Bearer Request | Yes | | 98 | Update Bearer Response | Yes | | 99 | Delete Bearer Request | Yes | | 100 | Delete Bearer Response | Yes | | 101 | Delete PDN Connection Set Request | Yes | | 102 | Delete PDN Connection Set Response | Yes | | 103 | PGW Downlink Triggering Notification | | | 104 | PGW Downlink Triggering Acknowledge | | | 105-127 | (Spare/Reserved) | - | | 128 | Identification Request | | | 129 | Identification Response | | | 130 | Context Request | Yes | | 131 | Context Response | Yes | | 132 | Context Acknowledge | Yes | | 133 | Forward Relocation Request | | | 134 | Forward Relocation Response | | | 135 | Forward Relocation Complete Notification | | | 136 | Forward Relocation Complete Acknowledge | | | 137 | Forward Access Context Notification | | | 138 | Forward Access Context Acknowledge | | | 139 | Relocation Cancel Request | | | 140 | Relocation Cancel Response | | | 141 | Configuration Transfer Tunnel | | | 142-148 | (Spare/Reserved) | - | | 149 | Detach Notification | Yes | | 150 | Detach Acknowledge | Yes | | 151 | CS Paging Indication | | | 152 | RAN Information Relay | | | 153 | Alert MME Notification | | | 154 | Alert MME Acknowledge | | | 155 | UE Activity Notification | | | 156 | UE Activity Acknowledge | | | 157 | ISR Status Indication | | | 158 | UE Registration Query Request | | | 159 | UE Registration Query Response | | | 160 | Create Forwarding Tunnel Request | | | 161 | Create Forwarding Tunnel Response | | | 162 | Suspend Notification | Yes | | 163 | Suspend Acknowledge | Yes | | 164 | Resume Notification | Yes | | 165 | Resume Acknowledge | Yes | | 166 | Create Indirect Data Forwarding Tunnel Request | | | 167 | Create Indirect Data Forwarding Tunnel Response | | | 168 | Delete Indirect Data Forwarding Tunnel Request | | | 169 | Delete Indirect Data Forwarding Tunnel Response | | | 170 | Release Access Bearers Request | Yes | | 171 | Release Access Bearers Response | Yes | | 172-175 | (Spare/Reserved) | - | | 176 | Downlink Data Notification | Yes | | 177 | Downlink Data Notification Acknowledge | Yes | | 178 | (Spare/Reserved) | - | | 179 | PGW Restart Notification | Yes | | 180 | PGW Restart Notification Acknowledge | Yes | | 181-199 | (Spare/Reserved) | - | | 200 | Update PDN Connection Set Request | Yes | | 201 | Update PDN Connection Set Response | Yes | | 202-210 | (Spare/Reserved) | - | | 211 | Modify Access Bearers Request | Yes | | 212 | Modify Access Bearers Response | Yes | | 213-230 | (Spare/Reserved) | - | | 231 | MBMS Session Start Request | | | 232 | MBMS Session Start Response | | | 233 | MBMS Session Update Request | | | 234 | MBMS Session Update Response | | | 235 | MBMS Session Stop Request | | | 236 | MBMS Session Stop Response | | | 237-239 | (Spare/Reserved) | - | | 240-247 | (Spare/Reserved) | - | | 248-255 | (Spare/Reserved) | - | ### Information Elements The following Information Elements marked with "Yes" are currently available with their own useful constructors. _Even there are some missing IEs, you can create any kind of IEs by using `ie.New()` function or by initializing ie.IE directly._ | ID | Name | Supported | |---------|----------------------------------------------------------------|-----------| | 0 | (Spare/Reserved) | - | | 1 | International Mobile Subscriber Identity (IMSI) | Yes | | 2 | Cause | Yes | | 3 | Recovery (Restart Counter) | Yes | | 4-34 | (Spare/Reserved) | - | | 35-50 | (Spare/Reserved) | - | | 51 | STN-SR | | | 52-70 | (Spare/Reserved) | - | | 71 | Access Point Name (APN) | Yes | | 72 | Aggregate Maximum Bit Rate (AMBR) | Yes | | 73 | EPS Bearer ID (EBI) | Yes | | 74 | IP Address | Yes | | 75 | Mobile Equipment Identity (MEI) | Yes | | 76 | MSISDN | Yes | | 77 | Indication | Yes | | 78 | Protocol Configuration Options (PCO) | Yes | | 79 | PDN Address Allocation (PAA) | Yes | | 80 | Bearer Level Quality of Service (Bearer QoS) | Yes | | 81 | Flow Quality of Service (Flow QoS) | Yes | | 82 | RAT Type | Yes | | 83 | Serving Network | Yes | | 84 | EPS Bearer Level Traffic Flow Template (Bearer TFT) | Yes | | 85 | Traffic Aggregation Description (TAD) | | | 86 | User Location Information (ULI) | Yes | | 87 | Fully Qualified Tunnel Endpoint Identifier (F-TEID) | Yes | | 88 | TMSI | Yes | | 89 | Global CN-Id | Yes | | 90 | S103 PDN Data Forwarding Info (S103PDF) | Yes | | 91 | S1-U Data Forwarding Info (S1UDF) | Yes | | 92 | Delay Value | Yes | | 93 | Bearer Context | Yes | | 94 | Charging ID | Yes | | 95 | Charging Characteristics | Yes | | 96 | Trace Information | | | 97 | Bearer Flags | Yes | | 98 | (Spare/Reserved) | - | | 99 | PDN Type | Yes | | 100 | Procedure Transaction ID | Yes | | 101 | (Spare/Reserved) | - | | 102 | (Spare/Reserved) | - | | 103 | MM Context (GSM Key and Triplets) | | | 104 | MM Context (UMTS Key, Used Cipher and Quintuplets) | | | 105 | MM Context (GSM Key, Used Cipher and Quintuplets) | | | 106 | MM Context (UMTS Key and Quintuplets) | | | 107 | MM Context (EPS Security Context, Quadruplets and Quintuplets) | | | 108 | MM Context (UMTS Key, Quadruplets and Quintuplets) | | | 109 | PDN Connection | | | 110 | PDU Numbers | | | 111 | Packet TMSI | Yes | | 112 | P-TMSI Signature | Yes | | 113 | Hop Counter | Yes | | 114 | UE Time Zone | Yes | | 115 | Trace Reference | Yes | | 116 | Complete Request Message | | | 117 | GUTI | Yes | | 118 | F-Container | | | 119 | F-Cause | | | 120 | PLMN ID | Yes | | 121 | Target Identification | | | 122 | (Spare/Reserved) | - | | 123 | Packet Flow ID | | | 124 | RAB Context | | | 125 | Source RNC PDCP Context Info | | | 126 | Port Number | Yes | | 127 | APN Restriction | Yes | | 128 | Selection Mode | Yes | | 129 | Source Identification | | | 130 | (Spare/Reserved) | - | | 131 | Change Reporting Action | | | 132 | Fully Qualified PDN Connection Set Identifier (FQ-CSID) | Yes | | 133 | Channel Needed | | | 134 | eMLPP Priority | | | 135 | Node Type | Yes | | 136 | Fully Qualified Domain Name (FQDN) | Yes | | 137 | Transaction Identifier (TI) | | | 138 | MBMS Session Duration | | | 139 | MBMS Service Area | | | 140 | MBMS Session Identifier | | | 141 | MBMS Flow Identifier | | | 142 | MBMS IP Multicast Distribution | | | 143 | MBMS Distribution Acknowledge | | | 144 | RFSP Index | | | 145 | User CSG Information (UCI) | Yes | | 146 | CSG Information Reporting Action | | | 147 | CSG ID | Yes | | 148 | CSG Membership Indication (CMI) | Yes | | 149 | Service Indicator | Yes | | 150 | Detach Type | Yes | | 151 | Local Distinguished Name (LDN) | Yes | | 152 | Node Features | Yes | | 153 | MBMS Time to Data Transfer | | | 154 | Throttling | Yes | | 155 | Allocation/Retention Priority (ARP) | Yes | | 156 | EPC Timer | Yes | | 157 | Signalling Priority Indication | | | 158 | Temporary Mobile Group Identity (TMGI) | | | 159 | Additional MM context for SRVCC | | | 160 | Additional flags for SRVCC | | | 161 | (Spare/Reserved) | - | | 162 | MDT Configuration | | | 163 | Additional Protocol Configuration Options (APCO) | | | 164 | Absolute Time of MBMS Data Transfer | | | 165 | H(e)NB Information Reporting | | | 166 | IPv4 Configuration Parameters (IP4CP) | | | 167 | Change to Report Flags | | | 168 | Action Indication | | | 169 | TWAN Identifier | | | 170 | ULI Timestamp | Yes | | 171 | MBMS Flags | | | 172 | RAN/NAS Cause | Yes | | 173 | CN Operator Selection Entity | | | 174 | Trusted WLAN Mode Indication | | | 175 | Node Number | | | 176 | Node Identifier | | | 177 | Presence Reporting Area Action | | | 178 | Presence Reporting Area Information | | | 179 | TWAN Identifier Timestamp | | | 180 | Overload Control Information | | | 181 | Load Control Information | | | 182 | Metric | | | 183 | Sequence Number | | | 184 | APN and Relative Capacity | | | 185 | WLAN Offloadability Indication | | | 186 | Paging and Service Information | Yes | | 187 | Integer Number | Yes | | 188 | Millisecond Time Stamp | | | 189 | Monitoring Event Information | | | 190 | ECGI List | | | 191 | Remote UE Context | | | 192 | Remote User ID | | | 193 | Remote UE IP information | | | 194 | CIoT Optimizations Support Indication | | | 195 | SCEF PDN Connection | | | 196 | Header Compression Configuration | | | 197 | Extended Protocol Configuration Options (ePCO) | | | 198 | Serving PLMN Rate Control | | | 199 | Counter | | | 200 | Mapped UE Usage Type | | | 201 | Secondary RAT Usage Data Report | | | 202 | UP Function Selection Indication Flags | | | 203 | Maximum Packet Loss Rate | | | 204 | APN Rate Control Status | | | 205 | Extended Trace Information | | | 206 | Monitoring Event Extension Information | | | 207 | Additional RRM Policy Index | | | 208 | V2X Context | | | 209 | PC5 QoS Parameters | | | 210 | Services Authorized | | | 211 | Bit Rate | | | 212 | PC5 QoS Flow | | | 213-253 | (Spare/Reserved) | - | | 254 | (Spare/Reserved) | - | | 255 | Private Extension | Yes | ================================================ FILE: gtpv2/bearer.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv2 import ( "net" ) // QoSProfile represents a QoS-related information that belongs to a Bearer. type QoSProfile struct { PCI, PVI bool PL, QCI uint8 // Max bit rate for Uplink and Donwlink MBRUL, MBRDL uint64 // Guaranteed bit rate for Uplink and Donwlink GBRUL, GBRDL uint64 } // Bearer represents a GTPv2 bearer. type Bearer struct { raddr net.Addr teidIn, teidOut uint32 EBI uint8 SubscriberIP, APN string ChargingID uint32 *QoSProfile } // NewBearer creates a new Bearer. func NewBearer(ebi uint8, apn string, qos *QoSProfile) *Bearer { return &Bearer{ EBI: ebi, APN: apn, QoSProfile: qos, } } // RemoteAddress returns the remote address associated with Bearer. func (b *Bearer) RemoteAddress() net.Addr { return b.raddr } // SetRemoteAddress sets the remote address associated with Bearer. func (b *Bearer) SetRemoteAddress(raddr net.Addr) { b.raddr = raddr } // IncomingTEID returns the incoming TEID associated with Bearer. func (b *Bearer) IncomingTEID() uint32 { return b.teidIn } // SetIncomingTEID sets the incoming TEID associated with Bearer. func (b *Bearer) SetIncomingTEID(teid uint32) { b.teidIn = teid } // OutgoingTEID returns the outgoing TEID associated with Bearer. func (b *Bearer) OutgoingTEID() uint32 { return b.teidOut } // SetOutgoingTEID sets the outgoing TEID associated with Bearer. func (b *Bearer) SetOutgoingTEID(teid uint32) { b.teidOut = teid } ================================================ FILE: gtpv2/conn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv2 import ( "context" "crypto/rand" "encoding/binary" "errors" "fmt" "io" "net" "sync" "time" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) // Conn represents a GTPv2-C connection. // // Conn provides the automatic handling of message by adding handlers to it with // AddHandler(s). See AddHandler for detailed usage. // // Conn also provides the functions to manage Sessions/Bearers that works over the // connection(=between a node to another). // See the docs of CreateSession, AddSession, DeleteSession methods for details. type Conn struct { mu sync.Mutex laddr net.Addr pktConn net.PacketConn *imsiSessionMap *iteiSessionMap localIfType uint8 validationEnabled bool closeCh chan struct{} *msgHandlerMap // sequence is the last SequenceNumber used in the request. // // TS29.274 7.6 Reliable Delivery of Signalling Messages; // The Sequence Number shall be unique for each outstanding Initial message sourced // from the same IP/UDP endpoint(=Conn). sequence uint32 // RestartCounter is the RestartCounter value in Recovery IE, which represents how many // times the GTPv2-C endpoint is restarted. RestartCounter uint8 } // NewConn creates a new Conn used for server. On client side, use Dial instead. func NewConn(laddr net.Addr, localIfType, counter uint8) *Conn { return &Conn{ mu: sync.Mutex{}, laddr: laddr, imsiSessionMap: newimsiSessionMap(), iteiSessionMap: newiteiSessionMap(), localIfType: localIfType, validationEnabled: true, closeCh: make(chan struct{}), msgHandlerMap: newDefaultMsgHandlerMap(), sequence: 0, RestartCounter: counter, } } // Dial sends Echo Request to raddr to check if the endpoint is alive and returns Conn. // // It does not bind the raddr to the underlying connection, which enables a Conn to // send to/receive from multiple peers with single laddr. // // If Echo exchange is unnecessary, use NewConn and ListenAndServe instead. func Dial(ctx context.Context, laddr, raddr net.Addr, localIfType, counter uint8) (*Conn, error) { c := &Conn{ mu: sync.Mutex{}, laddr: laddr, imsiSessionMap: newimsiSessionMap(), iteiSessionMap: newiteiSessionMap(), localIfType: localIfType, validationEnabled: true, closeCh: make(chan struct{}), msgHandlerMap: newDefaultMsgHandlerMap(), sequence: 0, RestartCounter: counter, } // setup underlying connection first. // not using net.Dial, as it binds src/dst IP:Port, which makes it harder to // handle multiple connections with a Conn. var err error c.pktConn, err = net.ListenPacket(c.laddr.Network(), c.laddr.String()) if err != nil { return nil, err } // send EchoRequest to raddr. if _, err := c.EchoRequest(raddr); err != nil { return nil, err } buf := make([]byte, 1500) // if no response coming within 3 seconds, returns error without retrying. if err := c.pktConn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil { return nil, err } n, raddr, err := c.pktConn.ReadFrom(buf) if err != nil { return nil, err } if err := c.pktConn.SetReadDeadline(time.Time{}); err != nil { return nil, err } // decode incoming message and let it be handled by default handler funcs. msg, err := message.Parse(buf[:n]) if err != nil { return nil, err } if err := c.handleMessage(raddr, msg); err != nil { return nil, err } go func() { if err := c.Serve(ctx); err != nil { logf("fatal error on Conn %s: %s", c.LocalAddr(), err) } }() return c, nil } // ListenAndServe creates a new GTPv2-C Conn and start serving background. func (c *Conn) ListenAndServe(ctx context.Context) error { err := c.Listen(ctx) if err != nil { return err } return c.listenAndServe(ctx) } // Listen creates a new GTPv2-C Conn func (c *Conn) Listen(ctx context.Context) error { var err error c.mu.Lock() c.pktConn, err = net.ListenPacket(c.laddr.Network(), c.laddr.String()) c.mu.Unlock() if err != nil { return err } return nil } func (c *Conn) listenAndServe(ctx context.Context) error { // TODO: this func is left for future enhancement. return c.Serve(ctx) } func (c *Conn) closed() <-chan struct{} { return c.closeCh } // Serve starts serving GTPv2 connection. func (c *Conn) Serve(ctx context.Context) error { go func() { select { // ctx is canceled or Close() is called case <-ctx.Done(): case <-c.closed(): } if err := c.pktConn.Close(); err != nil { logf("error closing the underlying conn: %s", err) } }() buf := make([]byte, 1500) for { n, raddr, err := c.pktConn.ReadFrom(buf) if err != nil { if errors.Is(err, io.EOF) { return nil } if errors.Is(err, net.ErrClosed) { return nil } return fmt.Errorf("error reading from Conn %s: %w", c.LocalAddr(), err) } raw := make([]byte, n) copy(raw, buf) go func() { msg, err := message.Parse(raw) if err != nil { logf("error parsing the message: %v, %x", err, raw) return } if err := c.handleMessage(raddr, msg); err != nil { logf("error handling message on Conn %s: %v", c.LocalAddr(), err) } }() } } // ReadFrom reads a packet from the connection, // copying the payload into p. It returns the number of // bytes copied into p and the return address that // was on the packet. // It returns the number of bytes read (0 <= n <= len(p)) // and any error encountered. Callers should always process // the n > 0 bytes returned before considering the error err. // ReadFrom can be made to time out and return // an Error with Timeout() == true after a fixed time limit; // see SetDeadline and SetReadDeadline. func (c *Conn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { return c.pktConn.ReadFrom(p) } // WriteTo writes a packet with payload p to addr. // WriteTo can be made to time out and return // an Error with Timeout() == true after a fixed time limit; // see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. func (c *Conn) WriteTo(p []byte, addr net.Addr) (n int, err error) { return c.pktConn.WriteTo(p, addr) } // Close closes the connection. // Any blocked Read or Write operations will be unblocked and return errors. func (c *Conn) Close() error { c.mu.Lock() defer c.mu.Unlock() close(c.closeCh) return nil } // LocalAddr returns the local network address. func (c *Conn) LocalAddr() net.Addr { return c.pktConn.LocalAddr() } // SetDeadline sets the read and write deadlines associated // with the connection. It is equivalent to calling both // SetReadDeadline and SetWriteDeadline. // // A deadline is an absolute time after which I/O operations // fail with a timeout (see type Error) instead of // blocking. The deadline applies to all future and pending // I/O, not just the immediately following call to Read or // Write. After a deadline has been exceeded, the connection // can be refreshed by setting a deadline in the future. // // An idle timeout can be implemented by repeatedly extending // the deadline after successful Read or Write calls. // // A zero value for t means I/O operations will not time out. func (c *Conn) SetDeadline(t time.Time) error { return c.pktConn.SetDeadline(t) } // SetReadDeadline sets the deadline for future Read calls // and any currently-blocked Read call. // A zero value for t means Read will not time out. func (c *Conn) SetReadDeadline(t time.Time) error { return c.pktConn.SetReadDeadline(t) } // SetWriteDeadline sets the deadline for future Write calls // and any currently-blocked Write call. // Even if write times out, it may return n > 0, indicating that // some of the data was successfully written. // A zero value for t means Write will not time out. func (c *Conn) SetWriteDeadline(t time.Time) error { return c.pktConn.SetWriteDeadline(t) } // AddHandler adds a message handler to Conn. // // By adding HandlerFunc, Conn(and Session, Bearer created over the Conn) will handle // the specified type of message with it's paired HandlerFunc when receiving. // Messages without registered handlers are just ignored and logged. // // This should be performed just after creating Conn, otherwise the user cannot retrieve // any values, which is in most cases vital to continue working as a node, from the incoming // message. // // The error returned from handler is just logged. Any important due should be done inside // the HandlerFunc before returning. This behavior might change in the future. // // HandlerFunc for EchoResponse and VersionNotSupportedIndication are registered by default. // These HandlerFunc can be overridden by specifying message.MsgTypeEchoResponse and/or // message.MsgTypeVersionNotSupportedIndication as msgType parameter. func (c *Conn) AddHandler(msgType uint8, fn HandlerFunc) { c.msgHandlerMap.store(msgType, fn) } // AddHandlers adds multiple handler funcs at a time, using a map. // The key of the map is message type of the GTPv2-C message. You can use MsgTypeFooBar // constants defined in this package as well as any raw uint8 values. // // See AddHandler for how the given handlers behave. func (c *Conn) AddHandlers(funcs map[uint8]HandlerFunc) { for msgType, fn := range funcs { c.msgHandlerMap.store(msgType, fn) } } func (c *Conn) handleMessage(senderAddr net.Addr, msg message.Message) error { if c.validationEnabled { if err := c.validate(senderAddr, msg); err != nil { return fmt.Errorf("failed to validate %s: %w", msg.MessageTypeName(), err) } } handle, ok := c.msgHandlerMap.load(msg.MessageType()) if !ok { return &HandlerNotFoundError{MsgType: msg.MessageTypeName()} } if err := handle(c, senderAddr, msg); err != nil { return fmt.Errorf("failed to handle %s: %w", msg.MessageTypeName(), err) } return nil } // EnableValidation turns on automatic validation of incoming message. // This is expected to be used only after DisableValidation() is used, as the validation // is enabled by default. // // Conn checks if; // // GTP Version is 2 // TEID is known to Conn // // Even the validation is failed, it does not return error to user. Instead, it just logs // and discards the packets so that the HandlerFunc won't get the invalid message. // Extra validations should be done in HandlerFunc. func (c *Conn) EnableValidation() { c.mu.Lock() defer c.mu.Unlock() c.validationEnabled = true } // DisableValidation turns off automatic validation of incoming message. // It is not recommended to use this except the node is in debugging mode. // // See EnableValidation for what are validated. func (c *Conn) DisableValidation() { c.mu.Lock() defer c.mu.Unlock() c.validationEnabled = false } func (c *Conn) validate(senderAddr net.Addr, msg message.Message) error { // check GTP version if msg.Version() != 2 { if err := c.VersionNotSupportedIndication(senderAddr, msg); err != nil { return fmt.Errorf("failed to respond with VersionNotSupportedIndication: %w", err) } return fmt.Errorf("received an invalid version(%d) of message: %v", msg.Version(), msg) } // check if TEID is known or not if teid := msg.TEID(); teid != 0 { if _, err := c.GetSessionByTEID(teid, senderAddr); err != nil { return &InvalidTEIDError{TEID: teid} } } return nil } // SendMessageTo sends a message to addr. // Unlike WriteTo, it sets the Sequence Number properly and returns the one used in the message. func (c *Conn) SendMessageTo(msg message.Message, addr net.Addr) (uint32, error) { seq := c.IncSequence() msg.SetSequenceNumber(seq) payload, err := message.Marshal(msg) if err != nil { seq = c.DecSequence() return seq, fmt.Errorf("failed to send %T: %w", msg, err) } if _, err := c.WriteTo(payload, addr); err != nil { seq = c.DecSequence() return seq, fmt.Errorf("failed to send %T: %w", msg, err) } return seq, nil } // IncSequence increments the SequenceNumber associated with Conn. func (c *Conn) IncSequence() uint32 { c.mu.Lock() defer c.mu.Unlock() c.sequence++ // SequenceNumber is 3-octet long if c.sequence > 0x7fffff { c.sequence = 0 } return c.sequence } // DecSequence decrements the SequenceNumber associated with Conn. func (c *Conn) DecSequence() uint32 { c.mu.Lock() defer c.mu.Unlock() c.sequence-- return c.sequence } // SequenceNumber returns the current(=last used) SequenceNumber associated with Conn. func (c *Conn) SequenceNumber() uint32 { c.mu.Lock() defer c.mu.Unlock() return c.sequence } // EchoRequest sends a EchoRequest. func (c *Conn) EchoRequest(raddr net.Addr) (uint32, error) { msg := message.NewEchoRequest(0, ie.NewRecovery(c.RestartCounter)) seq, err := c.SendMessageTo(msg, raddr) if err != nil { return 0, err } return seq, nil } // EchoResponse sends a EchoResponse in response to the EchoRequest. func (c *Conn) EchoResponse(raddr net.Addr, req message.Message) error { res := message.NewEchoResponse(0, ie.NewRecovery(c.RestartCounter)) if err := c.RespondTo(raddr, req, res); err != nil { return err } return nil } // VersionNotSupportedIndication sends VersionNotSupportedIndication message // in response to any kind of message.Message. func (c *Conn) VersionNotSupportedIndication(raddr net.Addr, req message.Message) error { res := message.NewVersionNotSupportedIndication(0, req.Sequence()) if err := c.RespondTo(raddr, req, res); err != nil { return err } return nil } // ParseCreateSession iterates through the ie and returns a session func (c *Conn) ParseCreateSession(raddr net.Addr, ies ...*ie.IE) (*Session, error) { // retrieve values from IEs given. sess := NewSession(raddr, &Subscriber{Location: &Location{}}) br := sess.GetDefaultBearer() var err error for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: sess.IMSI, err = i.IMSI() if err != nil { return nil, err } case ie.MSISDN: sess.MSISDN, err = i.MSISDN() if err != nil { return nil, err } case ie.MobileEquipmentIdentity: sess.IMEI, err = i.MobileEquipmentIdentity() if err != nil { return nil, err } case ie.ServingNetwork: sess.MCC, err = i.MCC() if err != nil { return nil, err } sess.MNC, err = i.MNC() if err != nil { return nil, err } case ie.AccessPointName: br.APN, err = i.AccessPointName() if err != nil { return nil, err } case ie.RATType: sess.RATType, err = i.RATType() if err != nil { return nil, err } case ie.FullyQualifiedTEID: it, err := i.InterfaceType() if err != nil { return nil, err } teid, err := i.TEID() if err != nil { return nil, err } sess.AddTEID(it, teid) if it == c.localIfType { c.RegisterSession(teid, sess) } case ie.BearerContext: switch i.Instance() { case 0: for _, child := range i.ChildIEs { switch child.Type { case ie.EPSBearerID: br.EBI, err = child.EPSBearerID() if err != nil { return nil, err } case ie.BearerQoS: br.PL, err = child.PriorityLevel() if err != nil { return nil, err } br.QCI, err = child.QCILabel() if err != nil { return nil, err } br.PCI = child.HasPCI() br.PVI = child.HasPVI() br.MBRUL, err = child.MBRForUplink() if err != nil { return nil, err } br.MBRDL, err = child.MBRForDownlink() if err != nil { return nil, err } br.GBRUL, err = child.GBRForUplink() if err != nil { return nil, err } br.GBRDL, err = child.GBRForUplink() if err != nil { return nil, err } case ie.FullyQualifiedTEID: it, err := child.InterfaceType() if err != nil { return nil, err } teid, err := child.TEID() if err != nil { return nil, err } sess.AddTEID(it, teid) case ie.BearerTFT: // XXX - do nothing for BearerTFT? } } case 1: // XXX - do nothing for BearerContextsToBeRemoved? } } } return sess, nil } // CreateSession sends a CreateSessionRequest and stores information given with IE // in the Session returned. // // After using this method, users don't need to call AddSession with the session // returned. // // By creating a Session with this method, the values in IEs given, such as TEID in F-TEID // are stored with "best effort". See the source code to see what kind information is // handled automatically in this method. // // Also, a Bearer named "default" is also created to be used as default bearer. // The default bearer can be retrieved by using GetDefaultBearer() or LookupBearerByName("default"). // // Note that this method doesn't care IEs given are sufficient or not, as the required IE // varies much depending on the context in which the Create Session Request is used. // In other words, any kind of IE can be put on the Create Session Request message using // this method. func (c *Conn) CreateSession(raddr net.Addr, ie ...*ie.IE) (*Session, uint32, error) { sess, err := c.ParseCreateSession(raddr, ie...) if err != nil { return nil, 0, err } // set IEs into CreateSessionRequest. msg := message.NewCreateSessionRequest(0, 0, ie...) seq, err := c.SendMessageTo(msg, raddr) if err != nil { return nil, 0, err } return sess, seq, nil } // DeleteSession sends a DeleteSessionRequest with TEID and IEs given. func (c *Conn) DeleteSession(teid uint32, sess *Session, ie ...*ie.IE) (uint32, error) { msg := message.NewDeleteSessionRequest(teid, 0, ie...) seq, err := c.SendMessageTo(msg, sess.peerAddr) if err != nil { return 0, err } return seq, nil } // ModifyBearer sends a ModifyBearerRequest with TEID and IEs given.. func (c *Conn) ModifyBearer(teid uint32, sess *Session, ie ...*ie.IE) (uint32, error) { msg := message.NewModifyBearerRequest(teid, 0, ie...) seq, err := c.SendMessageTo(msg, sess.peerAddr) if err != nil { return 0, err } return seq, nil } // DeleteBearer sends a DeleteBearerRequest TEID and with IEs given. func (c *Conn) DeleteBearer(teid uint32, sess *Session, ie ...*ie.IE) (uint32, error) { msg := message.NewDeleteBearerRequest(teid, 0, ie...) seq, err := c.SendMessageTo(msg, sess.peerAddr) if err != nil { return 0, err } return seq, nil } // RespondTo sends a message(specified with "toBeSent" param) in response to a message // (specified with "received" param). // // This exists to make it easier to handle SequenceNumber. func (c *Conn) RespondTo(raddr net.Addr, received, toBeSent message.Message) error { toBeSent.SetSequenceNumber(received.Sequence()) b := make([]byte, toBeSent.MarshalLen()) if err := toBeSent.MarshalTo(b); err != nil { return err } if _, err := c.WriteTo(b, raddr); err != nil { return err } return nil } // GetSessionByTEID returns Session looked up by TEID and sender of the message. func (c *Conn) GetSessionByTEID(teid uint32, peer net.Addr) (*Session, error) { session, ok := c.iteiSessionMap.load(teid) if !ok { return nil, &InvalidTEIDError{TEID: teid} } if peer.String() != session.peerAddrString { return nil, &InvalidTEIDError{TEID: teid} } return session, nil } // GetSessionByIMSI returns Session looked up by IMSI. func (c *Conn) GetSessionByIMSI(imsi string) (*Session, error) { if session, ok := c.imsiSessionMap.load(imsi); ok { return session, nil } return nil, &UnknownIMSIError{IMSI: imsi} } // GetIMSIByTEID returns IMSI associated with TEID and the peer node. func (c *Conn) GetIMSIByTEID(teid uint32, peer net.Addr) (string, error) { sess, err := c.GetSessionByTEID(teid, peer) if err != nil { return "", err } return sess.IMSI, nil } // RegisterSession registers session to Conn with its incoming TEID to // distinguish which session the incoming message are for. // // Incoming TEID(itei) should be the one with it's local interface type. // e.g., if the Conn is used for S-GW on S11 I/F, itei should be the one // with interface type=IFTypeS11S4SGWGTPC. func (c *Conn) RegisterSession(itei uint32, session *Session) { c.iteiSessionMap.store(itei, session) c.imsiSessionMap.store(session.IMSI, session) session.AddTEID(c.localIfType, itei) } // RemoveSession removes a session registered in a Conn. func (c *Conn) RemoveSession(session *Session) { c.imsiSessionMap.delete(session.IMSI) itei, err := session.GetTEID(c.localIfType) if err != nil { // if incoming TEID could not be found for some reason logf("failed to find incoming TEID in session: %+v", err) c.iteiSessionMap.rangeWithFunc(func(k, v interface{}) bool { s := v.(*Session) if s.IMSI == session.IMSI { c.iteiSessionMap.delete(k.(uint32)) } return true }) return } c.iteiSessionMap.delete(itei) } // RemoveSessionByIMSI removes a session looked up by IMSI. // // Use RemoveSession instead if you already have the Session in your hand. func (c *Conn) RemoveSessionByIMSI(imsi string) { sess, ok := c.imsiSessionMap.load(imsi) if !ok { logf("Session not found by IMSI: %s", imsi) return } c.RemoveSession(sess) } // NewSenderFTEID creates a new F-TEID with random TEID value that is unique within Conn. // To ensure the uniqueness, don't create in the other way if you once use this method. // This is meant to be used for creating F-TEID IE only for local interface type that is // specified at the creation of Conn. // // Note that in the case there's a lot of Session on the Conn, it may take a long // time to find a new unique value. // // TODO: optimize performance... func (c *Conn) NewSenderFTEID(v4, v6 string) (fteidIE *ie.IE) { var teid uint32 for try := uint32(0); try < 0xffff; try++ { const logEvery = 0xff if try&logEvery == logEvery { logf("Generating NewSenderFTEID crossed tries:%d", try) } t := generateRandomUint32() if t == 0 { continue } // Try to mark TEID as taken. Fails if something exists if ok := c.iteiSessionMap.tryStore(t, nil); !ok { continue } teid = t break } if teid == 0 { return nil } return ie.NewFullyQualifiedTEID(c.localIfType, teid, v4, v6) } func generateRandomUint32() uint32 { b := make([]byte, 4) if _, err := rand.Read(b); err != nil { return 0 } return binary.BigEndian.Uint32(b) } // Sessions returns all the sessions registered in Conn. func (c *Conn) Sessions() []*Session { var ss []*Session c.imsiSessionMap.rangeWithFunc(func(k, v interface{}) bool { ss = append(ss, v.(*Session)) return true }) return ss } // SessionCount returns the number of sessions registered in Conn. // // This may have some impact on performance in case of large number of Session exists. func (c *Conn) SessionCount() int { var count int c.imsiSessionMap.rangeWithFunc(func(k, v interface{}) bool { sess := v.(*Session) if sess.IsActive() { count++ } return true }) return count } // BearerCount returns the number of bearers registered in Conn. // // This may have some impact on performance in case of large number of Session and Bearer exist. func (c *Conn) BearerCount() int { var count int c.imsiSessionMap.rangeWithFunc(func(k, v interface{}) bool { sess := v.(*Session) if sess.IsActive() { count += sess.BearerCount() } return true }) return count } type imsiSessionMap struct { syncMap sync.Map } func newimsiSessionMap() *imsiSessionMap { return &imsiSessionMap{} } func (i *imsiSessionMap) store(imsi string, session *Session) { i.syncMap.Store(imsi, session) } func (i *imsiSessionMap) load(imsi string) (*Session, bool) { session, ok := i.syncMap.Load(imsi) if ok && session != nil { return session.(*Session), true } return nil, ok } func (i *imsiSessionMap) delete(imsi string) { i.syncMap.Delete(imsi) } func (i *imsiSessionMap) rangeWithFunc(fn func(imsi, session interface{}) bool) { i.syncMap.Range(fn) } type iteiSessionMap struct { syncMap sync.Map } func newiteiSessionMap() *iteiSessionMap { return &iteiSessionMap{} } func (t *iteiSessionMap) store(teid uint32, session *Session) { t.syncMap.Store(teid, session) } func (t *iteiSessionMap) tryStore(teid uint32, session *Session) bool { _, loaded := t.syncMap.LoadOrStore(teid, session) return !loaded } func (t *iteiSessionMap) load(teid uint32) (*Session, bool) { session, ok := t.syncMap.Load(teid) if ok && session != nil { return session.(*Session), true } return nil, ok } func (t *iteiSessionMap) delete(teid uint32) { t.syncMap.Delete(teid) } func (t *iteiSessionMap) rangeWithFunc(fn func(imsi, session interface{}) bool) { t.syncMap.Range(fn) } ================================================ FILE: gtpv2/conn_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv2_test import ( "context" "fmt" "log" "net" "testing" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) func setup(ctx context.Context, doneCh chan struct{}) (cliConn, srvConn *gtpv2.Conn, err error) { cliAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1"+gtpv2.GTPCPort) if err != nil { return nil, nil, err } srvAddr, err := net.ResolveUDPAddr("udp", "127.0.0.2"+gtpv2.GTPCPort) if err != nil { return nil, nil, err } srvCreated := make(chan struct{}) go func() { srvConn = gtpv2.NewConn(srvAddr, gtpv2.IFTypeS11S4SGWGTPC, 0) srvConn.AddHandler( message.MsgTypeCreateSessionRequest, func(c *gtpv2.Conn, cliAddr net.Addr, msg message.Message) error { csReq := msg.(*message.CreateSessionRequest) session := gtpv2.NewSession(cliAddr, >pv2.Subscriber{Location: >pv2.Location{}}) var otei uint32 if imsiIE := csReq.IMSI; imsiIE != nil { imsi, err := imsiIE.IMSI() if err != nil { return err } if imsi != "123451234567890" { return fmt.Errorf("unexpected IMSI: %s", imsi) } session.IMSI = imsi } else { return >pv2.RequiredIEMissingError{Type: ie.IMSI} } if fteidcIE := csReq.SenderFTEIDC; fteidcIE != nil { ip, err := fteidcIE.IPAddress() if err != nil { return err } if ip != "127.0.0.1" { return fmt.Errorf("unexpected IP in F-TEID: %s", ip) } ifType, err := fteidcIE.InterfaceType() if err != nil { return err } otei, err = fteidcIE.TEID() if err != nil { return err } session.AddTEID(ifType, otei) } else { return >pv2.RequiredIEMissingError{Type: ie.IMSI} } fTEID := srvConn.NewSenderFTEID("127.0.0.2", "") srvConn.RegisterSession(fTEID.MustTEID(), session) csRsp := message.NewCreateSessionResponse( otei, msg.Sequence(), ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), fTEID, ) if err := c.RespondTo(cliAddr, csReq, csRsp); err != nil { return err } if err := session.Activate(); err != nil { return err } doneCh <- struct{}{} return nil }, ) if err := srvConn.Listen(ctx); err != nil { log.Println(err) return } srvCreated <- struct{}{} if err := srvConn.Serve(ctx); err != nil { log.Println(err) return } }() select { case <-srvCreated: case <-time.After(2 * time.Second): fmt.Println("Timeout waiting for server creation") } cliConn, err = gtpv2.Dial(ctx, cliAddr, srvAddr, gtpv2.IFTypeS11MMEGTPC, 0) if err != nil { return nil, nil, err } return cliConn, srvConn, nil } func TestCreateSession(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() doneCh := make(chan struct{}) rspOK := make(chan struct{}) cliConn, srvConn, err := setup(ctx, doneCh) if err != nil { t.Fatal(err) } cliConn.AddHandler( message.MsgTypeCreateSessionResponse, func(c *gtpv2.Conn, srvAddr net.Addr, msg message.Message) error { if srvAddr.String() != "127.0.0.2"+gtpv2.GTPCPort { t.Errorf("invalid server address: %s", srvAddr) } if msg.Sequence() != cliConn.SequenceNumber() { t.Errorf("invalid sequence number. got: %d, want: %d", msg.Sequence(), cliConn.SequenceNumber()) } session, err := c.GetSessionByTEID(msg.TEID(), srvAddr) if err != nil { return err } csRsp := msg.(*message.CreateSessionResponse) if causeIE := csRsp.Cause; causeIE != nil { cause, err := causeIE.Cause() if err != nil { return err } if cause != gtpv2.CauseRequestAccepted { return >pv2.CauseNotOKError{ MsgType: csRsp.MessageTypeName(), Cause: cause, Msg: "something went wrong", } } } else { return >pv2.RequiredIEMissingError{Type: ie.Cause} } if fteidIE := csRsp.SenderFTEIDC; fteidIE != nil { it, err := fteidIE.InterfaceType() if err != nil { return err } if it != gtpv2.IFTypeS11S4SGWGTPC { return fmt.Errorf("invalid InterfaceType: %v", it) } otei, err := fteidIE.TEID() if err != nil { return err } session.AddTEID(it, otei) ip, err := fteidIE.IPAddress() if err != nil { return err } if ip != "127.0.0.2" { return fmt.Errorf("unexpected IP in F-TEID: %s", ip) } } else { return >pv2.RequiredIEMissingError{Type: ie.Cause} } if err := session.Activate(); err != nil { return err } rspOK <- struct{}{} return nil }, ) fTEID := cliConn.NewSenderFTEID("127.0.0.1", "") _, _, err = cliConn.CreateSession(srvConn.LocalAddr(), ie.NewIMSI("123451234567890"), fTEID) if err != nil { t.Fatal(err) } select { case <-rspOK: if count := cliConn.SessionCount(); count != 1 { t.Errorf("wrong SessionCount in cliConn. want %d, got: %d", 1, count) } if count := cliConn.BearerCount(); count != 1 { t.Errorf("wrong BearerCount in cliConn. want %d, got: %d", 1, count) } <-doneCh if count := srvConn.SessionCount(); count != 1 { t.Errorf("wrong SessionCount in srvConn. want %d, got: %d", 1, count) } if count := srvConn.BearerCount(); count != 1 { t.Errorf("wrong BearerCount in srvConn. want %d, got: %d", 1, count) } case <-time.After(5 * time.Second): t.Fatal("timed out while waiting for validating Create Session Response") } } ================================================ FILE: gtpv2/constants.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv2 // Fixes for the constants with wrong names (original ones are kept for compatibility). const ( ContIDMSSupportOfNetworkRequestedBearerControlIndicator uint16 = 5 // ContIDMSSupportofNetworkRequestedBearerControlIndicator ContIDIPAddressAllocationViaNASSignalling uint16 = 10 // ContIDIPaddressAllocationViaNASSignalling ContIDIPv4AddressAllocationViaDHCPv4 uint16 = 11 // ContIDIPv4addressAllocationViaDHCPv4 ContID3GPPPSDataOffUEStatus uint16 = 23 // ContID3GPPPSDataOffUEstatus SelectionModeMSOrNetworkProvidedAPNSubscribedVerified uint8 = 0 // SelectionModeMSorNetworkProvidedAPNSubscribedVerified ) // Registered UDP ports const ( GTPCPort = ":2123" GTPUPort = ":2152" ) // InterfaceType definitions. const ( IFTypeS1UeNodeBGTPU uint8 = iota IFTypeS1USGWGTPU IFTypeS12RNCGTPU IFTypeS12SGWGTPU IFTypeS5S8SGWGTPU IFTypeS5S8PGWGTPU IFTypeS5S8SGWGTPC IFTypeS5S8PGWGTPC IFTypeS5S8SGWPMIPv6 IFTypeS5S8PGWPMIPv6 IFTypeS11MMEGTPC IFTypeS11S4SGWGTPC IFTypeS10MMEGTPC IFTypeS3MMEGTPC IFTypeS3SGSNGTPC IFTypeS4SGSNGTPU IFTypeS4SGWGTPU IFTypeS4SGSNGTPC IFTypeS16SGSNGTPC IFTypeeNodeBGTPUForDL IFTypeeNodeBGTPUForUL IFTypeRNCGTPUForData IFTypeSGSNGTPUForData IFTypeSGWUPFGTPUForDL IFTypeSmMBMSGWGTPC IFTypeSnMBMSGWGTPC IFTypeSmMMEGTPC IFTypeSnSGSNGTPC IFTypeSGWGTPUForUL IFTypeSnSGSNGTPU IFTypeS2bePDGGTPC IFTypeS2bUePDGGTPU IFTypeS2bPGWGTPC IFTypeS2bUPGWGTPU IFTypeS2aTWANGTPU IFTypeS2aTWANGTPC IFTypeS2aPGWGTPC IFTypeS2aPGWGTPU IFTypeS11MMEGTPU IFTypeS11SGWGTPU ) // APN Restriction definitions. const ( APNRestrictionNoExistingContextsorRestriction uint8 = iota APNRestrictionPublic1 APNRestrictionPublic2 APNRestrictionPrivate1 APNRestrictionPrivate2 ) // Cause definitions. const ( _ uint8 = 0 _ uint8 = 1 CauseLocalDetach uint8 = 2 CauseCompleteDetach uint8 = 3 CauseRATChangedFrom3GPPToNon3GPP uint8 = 4 CauseISRDeactivation uint8 = 5 CauseErrorIndicationReceivedFromRNCeNodeBS4SGSNMME uint8 = 6 CauseIMSIDetachOnly uint8 = 7 CauseReactivationRequested uint8 = 8 CausePDNReconnectionToThisAPNDisallowed uint8 = 9 CauseAccessChangedFromNon3GPPTo3GPP uint8 = 10 CausePDNConnectionInactivityTimerExpires uint8 = 11 CausePGWNotResponding uint8 = 12 CauseNetworkFailure uint8 = 13 CauseQoSParameterMismatch uint8 = 14 _ uint8 = 15 CauseRequestAccepted uint8 = 16 CauseRequestAcceptedPartially uint8 = 17 CauseNewPDNTypeDueToNetworkPreference uint8 = 18 CauseNewPDNTypeDueToSingleAddressBearerOnly uint8 = 19 CauseContextNotFound uint8 = 64 CauseInvalidMessageFormat uint8 = 65 CauseVersionNotSupportedByNextPeer uint8 = 66 CauseInvalidLength uint8 = 67 CauseServiceNotSupported uint8 = 68 CauseMandatoryIEIncorrect uint8 = 69 CauseMandatoryIEMissing uint8 = 70 _ uint8 = 71 CauseSystemFailure uint8 = 72 CauseNoResourcesAvailable uint8 = 73 CauseSemanticErrorInTheTFTOperation uint8 = 74 CauseSyntacticErrorInTheTFTOperation uint8 = 75 CauseSemanticErrorsInPacketFilters uint8 = 76 CauseSyntacticErrorsInPacketFilters uint8 = 77 CauseMissingOrUnknownAPN uint8 = 78 _ uint8 = 79 CauseGREKeyNotFound uint8 = 80 CauseRelocationFailure uint8 = 81 CauseDeniedInRAT uint8 = 82 CausePreferredPDNTypeNotSupported uint8 = 83 CauseAllDynamicAddressesAreOccupied uint8 = 84 CauseUEContextWithoutTFTAlreadyActivated uint8 = 85 CauseProtocolTypeNotSupported uint8 = 86 CauseUENotResponding uint8 = 87 CauseUERefuses uint8 = 88 CauseServiceDenied uint8 = 89 CauseUnableToPageUE uint8 = 90 CauseNoMemoryAvailable uint8 = 91 CauseUserAuthenticationFailed uint8 = 92 CauseAPNAccessDeniedNoSubscription uint8 = 93 CauseRequestRejectedReasonNotSpecified uint8 = 94 CausePTMSISignatureMismatch uint8 = 95 CauseIMSIIMEINotKnown uint8 = 96 CauseSemanticErrorInTheTADOperation uint8 = 97 CauseSyntacticErrorInTheTADOperation uint8 = 98 _ uint8 = 99 CauseRemotePeerNotResponding uint8 = 100 CauseCollisionWithNetworkInitiatedRequest uint8 = 101 CauseUnableToPageUEDueToSuspension uint8 = 102 CauseConditionalIEMissing uint8 = 103 CauseAPNRestrictionTypeIncompatibleWithCurrentlyActivePDNConnection uint8 = 104 CauseInvalidOverallLengthOfTheTriggeredResponseMessageAndAPiggybackedInitialMessage uint8 = 105 CauseDataForwardingNotSupported uint8 = 106 CauseInvalidReplyFromRemotePeer uint8 = 107 CauseFallbackToGTPv1 uint8 = 108 CauseInvalidPeer uint8 = 109 CauseTemporarilyRejectedDueToHandoverTAURAUProcedureInProgress uint8 = 110 CauseModificationsNotLimitedToS1UBearers uint8 = 111 CauseRequestRejectedForAPMIPv6Reason uint8 = 112 CauseAPNCongestion uint8 = 113 CauseBearerHandlingNotSupported uint8 = 114 CauseUEAlreadyReattached uint8 = 115 CauseMultiplePDNConnectionsForAGivenAPNNotAllowed uint8 = 116 CauseTargetAccessRestrictedForTheSubscriber uint8 = 117 _ uint8 = 118 CauseMMESGSNRefusesDueToVPLMNPolicy uint8 = 119 CauseGTPCEntityCongestion uint8 = 120 CauseLateOverlappingRequest uint8 = 121 CauseTimedOutRequest uint8 = 122 CauseUEIsTemporarilyNotReachableDueToPowerSaving uint8 = 123 CauseRelocationFailureDueToNASMessageRedirection uint8 = 124 CauseUENotAuthorisedByOCSOrExternalAAAServer uint8 = 125 CauseMultipleAccessesToAPDNConnectionNotAllowed uint8 = 126 CauseRequestRejectedDueToUECapability uint8 = 127 CauseS1UPathFailure uint8 = 128 ) // CSG Membership Indication definitions. const ( CMINonCSG uint8 = iota CMICSG ) // Detach Type definitions. const ( _ uint8 = iota DetachTypePS DetachTypeCombinedPSCS ) // Node-ID Type definitions. const ( NodeIDIPv4 uint8 = iota NodeIDIPv6 NodeIDOther ) // Node Type definitions. const ( NodeTypeSGSN uint8 = iota NodeTypeMME ) // Protocol ID definitions. // For more identifiers, see RFC 3232. const ( ProtoIDLCP uint16 = 0xc021 ProtoIDPAP uint16 = 0xc023 ProtoIDCHAP uint16 = 0xc223 ProtoIDIPCP uint16 = 0x8021 ) // Container ID definitions. const ( _ uint16 = iota ContIDPCSCFIPv6AddressRequest ContIDIMCNSubsystemSignalingFlag ContIDDNSServerIPv6AddressRequest ContIDNotSupported ContIDMSSupportofNetworkRequestedBearerControlIndicator _ ContIDDSMIPv6HomeAgentAddressRequest ContIDDSMIPv6HomeNetworkPrefixRequest ContIDDSMIPv6IPv4HomeAgentAddressRequest ContIDIPaddressAllocationViaNASSignalling ContIDIPv4addressAllocationViaDHCPv4 ContIDPCSCFIPv4AddressRequest ContIDDNSServerIPv4AddressRequest ContIDMSISDNRequest ContIDIFOMSupportRequest ContIDIPv4LinkMTURequest ContIDMSSupportOfLocalAddressInTFTIndicator ContIDPCSCFReselectionSupport ContIDNBIFOMRequestIndicator ContIDNBIFOMMode ContIDNonIPLinkMTURequest ContIDAPNRateControlSupportIndicator ContID3GPPPSDataOffUEstatus ContIDReliableDataServiceRequestIndicator ContIDAdditionalAPNRateControlForExceptionDataSupportIndicator ContIDPDUSessionID _ _ _ _ _ ContIDEthernetFramePayloadMTURequest ContIDUnstructuredLinkMTURequest ContID5GSMCauseValue ) // Configuration Protocol definitions. const ( ConfigProtocolPPPWithIP uint8 = 0 ) // PDN Type definitions. const ( _ uint8 = iota PDNTypeIPv4 PDNTypeIPv6 PDNTypeIPv4v6 PDNTypeNonIP ) // Protocol Type definitions. const ( _ uint8 = iota ProtoTypeS1APCause ProtoTypeEMMCause ProtoTypeESMCause ProtoTypeDiameterCause ProtoTypeIKEv2Cause ) // Cause Type definitions. const ( CauseTypeRadioNetworkLayer uint8 = iota CauseTypeTransportLayer CauseTypeNAS CauseTypeProtocol CauseTypeMiscellaneous ) // RAT Type definitions. const ( _ uint8 = iota RATTypeUTRAN RATTypeGERAN RATTypeWLAN RATTypeGAN RATTypeHSPAEvolution RATTypeEUTRAN RATTypeVirtual RATTypeEUTRANNBIoT RATTypeLTEM RATTypeNR ) // SelectionMode definitions. const ( SelectionModeMSorNetworkProvidedAPNSubscribedVerified uint8 = iota SelectionModeMSProvidedAPNSubscriptionNotVerified SelectionModeNetworkProvidedAPNSubscriptionNotVerified ) // Service Indicator definitions. const ( _ uint8 = iota ServiceIndCSCall ServiceIndSMS ) // Access Mode definitions. const ( AccessModeClosed uint8 = iota AccessModeHybrid ) // Daylight Saving Time definitions. const ( DaylightSavingNoAdjustment uint8 = iota DaylightSavingPlusOneHour DaylightSavingPlusTwoHours ) ================================================ FILE: gtpv2/doc.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Package gtpv2 provides the simple and painless handling of GTPv2-C protocol in pure Golang. // // Please see README.md for detailed usage of the APIs provided by this package. // // https://github.com/wmnsk/go-gtp/blob/main/gtpv2/README.md package gtpv2 ================================================ FILE: gtpv2/errors.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv2 import ( "errors" "fmt" "github.com/wmnsk/go-gtp/gtpv2/message" ) var ( // ErrTEIDNotFound indicates that TEID is not registered for the interface specified. ErrTEIDNotFound = errors.New("no TEID found") // ErrTimeout indicates that a handler failed to complete its work due to the // absence of message expected to come from another endpoint. ErrTimeout = errors.New("timed out") ) // CauseNotOKError indicates that the value in Cause IE is not OK. type CauseNotOKError struct { MsgType string Cause uint8 Msg string } // Error returns error cause with message. func (e *CauseNotOKError) Error() string { return fmt.Sprintf("got non-OK Cause: %d in %s; %s", e.Cause, e.MsgType, e.Msg) } // RequiredIEMissingError indicates that the IE required is missing. type RequiredIEMissingError struct { Type uint8 } // Error returns error with missing IE type. func (e *RequiredIEMissingError) Error() string { return fmt.Sprintf("required IE missing: %d", e.Type) } // RequiredParameterMissingError indicates that no Bearer found by lookup methods. type RequiredParameterMissingError struct { Name, Msg string } // Error returns missing parameter with message. func (e *RequiredParameterMissingError) Error() string { return fmt.Sprintf("required parameter: %s is missing. %s", e.Name, e.Msg) } // UnexpectedTypeError indicates that the type of incoming message is not expected. type UnexpectedTypeError struct { Msg message.Message } // Error returns violating message type. func (e *UnexpectedTypeError) Error() string { return fmt.Sprintf("got unexpected type of message: %T", e.Msg) } // UnexpectedIEError indicates that the type of incoming message is not expected. type UnexpectedIEError struct { IEType uint8 } // Error returns violating message type. func (e *UnexpectedIEError) Error() string { return fmt.Sprintf("got unexpected type of message: %T", e.IEType) } // InvalidVersionError indicates that the version of the message specified by the user // is not acceptable for the receiver. type InvalidVersionError struct { Version int } // Error returns violationg version. func (e *InvalidVersionError) Error() string { return fmt.Sprintf("version: %d is not acceptable for the receiver", e.Version) } // InvalidSequenceError indicates that the Sequence Number is invalid. type InvalidSequenceError struct { Seq uint32 } // Error returns violating Sequence Number. func (e *InvalidSequenceError) Error() string { return fmt.Sprintf("got invalid Sequence Number: %d", e.Seq) } // InvalidTEIDError indicates that the TEID value is different from expected one or // not registered in TEIDMap. type InvalidTEIDError struct { TEID uint32 } // Error returns violating TEID. func (e *InvalidTEIDError) Error() string { return fmt.Sprintf("got invalid TEID: %#08x", e.TEID) } // UnknownIMSIError indicates that the IMSI is different from expected one. type UnknownIMSIError struct { IMSI string } // Error returns violating IMSI. func (e *UnknownIMSIError) Error() string { return fmt.Sprintf("got unknown IMSI: %s", e.IMSI) } // UnknownAPNError indicates that the APN is different from expected one. type UnknownAPNError struct { APN string } // Error returns violating APN. func (e *UnknownAPNError) Error() string { return fmt.Sprintf("got unknown APN: %s", e.APN) } // InvalidSessionError indicates that something went wrong with Session. type InvalidSessionError struct { IMSI string } // Error returns message with IMSI associated with Session if available. func (e *InvalidSessionError) Error() string { return fmt.Sprintf("invalid session, IMSI: %s", e.IMSI) } // BearerNotFoundError indicates that no Bearer found by lookup methods. type BearerNotFoundError struct { IMSI string } // Error returns message with IMSI associated with Bearer if available. func (e *BearerNotFoundError) Error() string { return fmt.Sprintf("no Bearer found: %s", e.IMSI) } // HandlerNotFoundError indicates that the handler func is not registered in *Conn // for the incoming GTPv2 message. In usual cases this error should not be taken // as fatal, as the other endpoint can make your program stop working just by // sending unregistered message. type HandlerNotFoundError struct { MsgType string } // Error returns violating message type to handle. func (e *HandlerNotFoundError) Error() string { return fmt.Sprintf("no handlers found for incoming message: %s, ignoring", e.MsgType) } ================================================ FILE: gtpv2/handlers.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv2 import ( "net" "sync" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) // HandlerFunc is a handler for specific GTPv2-C message. type HandlerFunc func(c *Conn, senderAddr net.Addr, msg message.Message) error type msgHandlerMap struct { syncMap sync.Map } func (m *msgHandlerMap) store(msgType uint8, handler HandlerFunc) { m.syncMap.Store(msgType, handler) } func (m *msgHandlerMap) load(msgType uint8) (HandlerFunc, bool) { handler, ok := m.syncMap.Load(msgType) if !ok { return nil, false } return handler.(HandlerFunc), true } func newMsgHandlerMap(m map[uint8]HandlerFunc) *msgHandlerMap { mhm := &msgHandlerMap{syncMap: sync.Map{}} for k, v := range m { mhm.store(k, v) } return mhm } func newDefaultMsgHandlerMap() *msgHandlerMap { return newMsgHandlerMap( map[uint8]HandlerFunc{ message.MsgTypeEchoRequest: handleEchoRequest, message.MsgTypeEchoResponse: handleEchoResponse, message.MsgTypeVersionNotSupportedIndication: handleVersionNotSupportedIndication, }, ) } func handleEchoRequest(c *Conn, senderAddr net.Addr, msg message.Message) error { // this should never happen, as the type should have been assured by // msgHandlerMap before this function is called. if _, ok := msg.(*message.EchoRequest); !ok { return &UnexpectedTypeError{Msg: msg} } // respond with EchoResponse. return c.RespondTo( senderAddr, msg, message.NewEchoResponse(0, ie.NewRecovery(c.RestartCounter)), ) } func handleEchoResponse(c *Conn, senderAddr net.Addr, msg message.Message) error { // this should never happen, as the type should have been assured by // msgHandlerMap before this function is called. if _, ok := msg.(*message.EchoResponse); !ok { return &UnexpectedTypeError{Msg: msg} } // do nothing. return nil } func handleVersionNotSupportedIndication(c *Conn, senderAddr net.Addr, msg message.Message) error { // this should never happen, as the type should have been assured by // msgHandlerMap before this function is called. if _, ok := msg.(*message.VersionNotSupportedIndication); !ok { return &UnexpectedTypeError{Msg: msg} } // let's just return err anyway. return &InvalidVersionError{Version: msg.Version()} } ================================================ FILE: gtpv2/helpers_test.go ================================================ package gtpv2_test import ( "fmt" "math" "net" "strconv" "testing" "github.com/wmnsk/go-gtp/gtpv2" ) var testConn *gtpv2.Conn var sessions []*gtpv2.Session var dummyAddr net.Addr = &net.UDPAddr{IP: net.IP{0x00, 0x00, 0x00, 0x00}, Port: 2123} func init() { testConn = gtpv2.NewConn(dummyAddr, gtpv2.IFTypeS11MMEGTPC, 0) sessions = []*gtpv2.Session{ gtpv2.NewSession(dummyAddr, >pv2.Subscriber{IMSI: "001011234567891"}), gtpv2.NewSession(dummyAddr, >pv2.Subscriber{IMSI: "001011234567892"}), gtpv2.NewSession(dummyAddr, >pv2.Subscriber{IMSI: "001011234567893"}), gtpv2.NewSession(dummyAddr, >pv2.Subscriber{IMSI: "001011234567894"}), } for i, sess := range sessions { _ = sess.Activate() sess.AddTEID(gtpv2.IFTypeS11MMEGTPC, uint32(i+1)) testConn.RegisterSession(uint32(i+1), sess) } } func TestSessionCount(t *testing.T) { if want, got := testConn.SessionCount(), len(sessions); want != got { t.Errorf("SessionCount is invalid. want: %d, got: %d", want, got) } } func TestGetSessionByIMSI_GetTEID(t *testing.T) { for i := 1; i <= testConn.SessionCount(); i++ { lastDigit := strconv.Itoa(i) sess, err := testConn.GetSessionByIMSI("00101123456789" + lastDigit) if err != nil { t.Fatal(err) } teid, err := sess.GetTEID(gtpv2.IFTypeS11MMEGTPC) if err != nil { t.Fatal(err) } if teid != uint32(i) { t.Errorf("Got wrong TEID at %d, %d, %s", i, teid, sess.IMSI) } } } func BenchmarkAddSession(b *testing.B) { for k := 0.; k < 6; k++ { existingSessions := int(math.Pow(10, k)) benchConn := gtpv2.NewConn(dummyAddr, gtpv2.IFTypeS11MMEGTPC, 0) for i := 0; i < existingSessions; i++ { imsi := fmt.Sprintf("%015d", i) benchConn.RegisterSession(0, gtpv2.NewSession(dummyAddr, >pv2.Subscriber{IMSI: imsi})) } b.Run(fmt.Sprintf("%d", existingSessions), func(b *testing.B) { for i := 1; i <= b.N; i++ { benchConn.RegisterSession(0, gtpv2.NewSession(dummyAddr, >pv2.Subscriber{IMSI: "001011234567891"})) } }) } } func TestGetSessionByTEID(t *testing.T) { for i := 1; i <= testConn.SessionCount(); i++ { sess, err := testConn.GetSessionByTEID(uint32(i), dummyAddr) if err != nil { t.Fatal(err) } lastDigit := strconv.Itoa(i) if string(sess.IMSI[14]) != lastDigit { t.Errorf("Got wrong session at %d, %s", i, sess.IMSI) } } } func TestGetIMSIByTEID(t *testing.T) { for i := 1; i <= testConn.SessionCount(); i++ { imsi, err := testConn.GetIMSIByTEID(uint32(i), dummyAddr) if err != nil { t.Fatal(err) } lastDigit := strconv.Itoa(i) if string(imsi[14]) != lastDigit { t.Errorf("Got wrong IMSI at %d, %s", i, imsi) } } } func TestRemoveSession(t *testing.T) { testConn.RemoveSession(sessions[0]) if testConn.SessionCount() != len(sessions)-1 { t.Errorf("Session not removed expectedly: %d, %v", testConn.SessionCount(), testConn.Sessions()) } for i := 2; i <= testConn.SessionCount(); i++ { sess, err := testConn.GetSessionByTEID(uint32(i), dummyAddr) if err != nil { t.Fatal(err) } lastDigit := strconv.Itoa(i) if string(sess.IMSI[14]) != lastDigit { t.Errorf("Got wrong session at %d, %s", i, sess.IMSI) } } // add the session again s := gtpv2.NewSession(dummyAddr, >pv2.Subscriber{IMSI: "001011234567891"}) _ = s.Activate() s.AddTEID(gtpv2.IFTypeS11MMEGTPC, uint32(0)) testConn.RegisterSession(0, s) } func TestRemoveSessionByIMSI(t *testing.T) { testConn.RemoveSessionByIMSI("001011234567891") if testConn.SessionCount() != len(sessions)-1 { t.Errorf("Session not removed expectedly: %d, %v", testConn.SessionCount(), testConn.Sessions()) } for i := 2; i <= testConn.SessionCount(); i++ { sess, err := testConn.GetSessionByTEID(uint32(i), dummyAddr) if err != nil { t.Fatal(err) } lastDigit := strconv.Itoa(i) if string(sess.IMSI[14]) != lastDigit { t.Errorf("Got wrong session at %d, %s", i, sess.IMSI) } } // add the session again s := gtpv2.NewSession(dummyAddr, >pv2.Subscriber{IMSI: "001011234567891"}) _ = s.Activate() s.AddTEID(gtpv2.IFTypeS11MMEGTPC, uint32(0)) testConn.RegisterSession(0, s) } ================================================ FILE: gtpv2/ie/ambr.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewAggregateMaximumBitRate creates a new AggregateMaximumBitRate IE. func NewAggregateMaximumBitRate(up, down uint32) *IE { v := NewAggregateMaximumBitRateFields(up, down) b, err := v.Marshal() if err != nil { return nil } return New(AggregateMaximumBitRate, 0x00, b) } // AggregateMaximumBitRate returns AggregateMaximumBitRate in AggregateMaximumBitRateFields type if the type of IE matches. func (i *IE) AggregateMaximumBitRate() (*AggregateMaximumBitRateFields, error) { switch i.Type { case AggregateMaximumBitRate: return ParseAggregateMaximumBitRateFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // AggregateMaximumBitRateFields is a set of fields in AggregateMaximumBitRate IE. type AggregateMaximumBitRateFields struct { APNAMBRForUplink uint32 APNAMBRForDownlink uint32 } // NewAggregateMaximumBitRateFields creates a new AggregateMaximumBitRateFields. func NewAggregateMaximumBitRateFields(up, down uint32) *AggregateMaximumBitRateFields { return &AggregateMaximumBitRateFields{ APNAMBRForUplink: up, APNAMBRForDownlink: down, } } // Marshal serializes AggregateMaximumBitRateFields. func (f *AggregateMaximumBitRateFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes AggregateMaximumBitRateFields. func (f *AggregateMaximumBitRateFields) MarshalTo(b []byte) error { if len(b) < 8 { return io.ErrUnexpectedEOF } binary.BigEndian.PutUint32(b[0:4], f.APNAMBRForUplink) binary.BigEndian.PutUint32(b[4:8], f.APNAMBRForDownlink) return nil } // ParseAggregateMaximumBitRateFields decodes AggregateMaximumBitRateFields. func ParseAggregateMaximumBitRateFields(b []byte) (*AggregateMaximumBitRateFields, error) { f := &AggregateMaximumBitRateFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into AggregateMaximumBitRateFields. func (f *AggregateMaximumBitRateFields) UnmarshalBinary(b []byte) error { if len(b) < 8 { return io.ErrUnexpectedEOF } f.APNAMBRForUplink = binary.BigEndian.Uint32(b[0:4]) f.APNAMBRForDownlink = binary.BigEndian.Uint32(b[4:8]) return nil } // MarshalLen returns the serial length of AggregateMaximumBitRateFields in int. func (f *AggregateMaximumBitRateFields) MarshalLen() int { return 8 } // AggregateMaximumBitRateUp returns AggregateMaximumBitRate for Uplink // if the type of IE matches. func (i *IE) AggregateMaximumBitRateUp() (uint32, error) { if i.Type != AggregateMaximumBitRate { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload[0:4]), nil } // MustAggregateMaximumBitRateUp returns AggregateMaximumBitRateUp in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustAggregateMaximumBitRateUp() uint32 { v, _ := i.AggregateMaximumBitRateUp() return v } // AggregateMaximumBitRateDown returns AggregateMaximumBitRate for Downlink // if the type of IE matches. func (i *IE) AggregateMaximumBitRateDown() (uint32, error) { if i.Type != AggregateMaximumBitRate { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 8 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload[4:8]), nil } // MustAggregateMaximumBitRateDown returns AggregateMaximumBitRateDown in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustAggregateMaximumBitRateDown() uint32 { v, _ := i.AggregateMaximumBitRateDown() return v } ================================================ FILE: gtpv2/ie/apn-restriction.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewAPNRestriction creates a new APNRestriction IE. func NewAPNRestriction(restriction uint8) *IE { return NewUint8IE(APNRestriction, restriction) } // APNRestriction returns APNRestriction in uint8 if the type of IE matches. func (i *IE) APNRestriction() (uint8, error) { if i.Type != APNRestriction { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustAPNRestriction returns APNRestriction in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustAPNRestriction() uint8 { v, _ := i.APNRestriction() return v } // RestrictionType returns RestrictionType in uint8 if the type of IE matches. func (i *IE) RestrictionType() (uint8, error) { return i.APNRestriction() } ================================================ FILE: gtpv2/ie/apn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewAccessPointName creates a new AccessPointName IE. func NewAccessPointName(apn string) *IE { return NewFQDNIE(AccessPointName, apn) } // AccessPointName returns AccessPointName in string if the type of IE matches. func (i *IE) AccessPointName() (string, error) { if i.Type != AccessPointName { return "", &InvalidTypeError{Type: i.Type} } return i.ValueAsFQDN() } // MustAccessPointName returns AccessPointName in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustAccessPointName() string { v, _ := i.AccessPointName() return v } ================================================ FILE: gtpv2/ie/arp.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewAllocationRetensionPriority creates a new AllocationRetensionPriority IE. func NewAllocationRetensionPriority(pci, pl, pvi uint8) *IE { i := New(AllocationRetensionPriority, 0x00, make([]byte, 1)) i.Payload[0] |= (pci << 6 & 0x40) | (pl << 2 & 0x3c) | (pvi & 0x01) return i } // AllocationRetensionPriority returns AllocationRetensionPriority in uint8 if the type of IE matches. func (i *IE) AllocationRetensionPriority() (uint8, error) { if i.Type != AllocationRetensionPriority { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // HasPVI reports whether an IE has PVI bit. func (i *IE) HasPVI() bool { v, err := i.AllocationRetensionPriority() if err != nil { return false } return has1stBit(v) } // HasPCI reports whether an IE has PCI bit. func (i *IE) HasPCI() bool { v, err := i.AllocationRetensionPriority() if err != nil { return false } return has7thBit(v) } // PriorityLevel returns PriorityLevel in uint8 if the type of IE matches. func (i *IE) PriorityLevel() (uint8, error) { switch i.Type { case AllocationRetensionPriority: v, err := i.AllocationRetensionPriority() if err != nil { return 0, err } return (v & 0x3c) >> 2, nil case BearerQoS: v, err := i.BearerQoS() if err != nil { return 0, err } return (v.ARP & 0x3c) >> 2, nil default: return 0, &InvalidTypeError{Type: i.Type} } } // PreemptionVulnerability reports whether the preemption vulnerability is set to enabled if the type of IE matches. // // Deprecated: use HasPVI instead. func (i *IE) PreemptionVulnerability() bool { return i.HasPVI() } // PreemptionCapability reports whether the preemption capability is set to enabled if the type of IE matches. // // Deprecated: use HasPCI instead. func (i *IE) PreemptionCapability() bool { return i.HasPCI() } ================================================ FILE: gtpv2/ie/bearer-context.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewBearerContext creates a new BearerContext IE. func NewBearerContext(ies ...*IE) *IE { return NewGroupedIE(BearerContext, ies...) } // NewBearerContextWithinCreateBearerRequest creates a new BearerContext used within CreateBearerRequest. func NewBearerContextWithinCreateBearerRequest(ebi, tft, qos, chargeID, flags, pco, epco, mplr *IE, fTEIDs ...*IE) *IE { ies := []*IE{ebi, tft, qos, chargeID, flags, pco, epco, mplr} ies = append(ies, fTEIDs...) return NewBearerContext(ies...) } // NewBearerContextWithinCreateBearerResponse creates a new BearerContext used within CreateBearerResponse. func NewBearerContextWithinCreateBearerResponse(ebi, cause, pco, rannasCause, epco *IE, fTEIDs ...*IE) *IE { ies := []*IE{ebi, cause, pco, rannasCause, epco} ies = append(ies, fTEIDs...) return NewBearerContext(ies...) } // NewBearerContextWithinDeleteBearerRequest creates a new BearerContext used within DeleteBearerRequest. func NewBearerContextWithinDeleteBearerRequest(ebi, cause *IE) *IE { return NewBearerContext(ebi, cause) } // NewBearerContextWithinDeleteBearerResponse creates a new BearerContext used within DeleteBearerResponse. func NewBearerContextWithinDeleteBearerResponse(ebi, cause, pco, rannasCause, epco *IE) *IE { return NewBearerContext(ebi, cause, pco, rannasCause, epco) } // NewBearerContextWithinModifyBearerCommand creates a new BearerContext used within ModifyBearerCommand. func NewBearerContextWithinModifyBearerCommand(ebi, qos *IE) *IE { return NewBearerContext(ebi, qos) } // NewBearerContextWithinUpdateBearerRequest creates a new BearerContext used within UpdateBearerRequest. func NewBearerContextWithinUpdateBearerRequest(ebi, tft, qos, flags, pco, apco, epco, mplr *IE) *IE { return NewBearerContext(ebi, tft, qos, flags, pco, apco, epco, mplr) } // NewBearerContextWithinUpdateBearerResponse creates a new BearerContext used within UpdateBearerResponse. func NewBearerContextWithinUpdateBearerResponse(ebi, cause, pco, rannasCause, epco *IE, fTEIDs ...*IE) *IE { ies := []*IE{ebi, cause, pco, rannasCause, epco} ies = append(ies, fTEIDs...) return NewBearerContext(ies...) } // NewBearerContextWithinDeleteBearerCommand creates a new BearerContext used within DeleteBearerCommand. func NewBearerContextWithinDeleteBearerCommand(ebi, flags, rannasCause *IE) *IE { return NewBearerContext(ebi, flags, rannasCause) } // NewBearerContextWithinDeleteBearerFailureIndication creates a new BearerContext used within DeleteBearerFailureIndication. func NewBearerContextWithinDeleteBearerFailureIndication(ebi, cause *IE) *IE { return NewBearerContext(ebi, cause) } // NewBearerContextWithinCreateIndirectDataForwardingTunnelRequest creates a new BearerContext used within CreateIndirectDataForwardingTunnelRequest. func NewBearerContextWithinCreateIndirectDataForwardingTunnelRequest(ebi *IE, fTEIDs ...*IE) *IE { ies := []*IE{ebi} ies = append(ies, fTEIDs...) return NewBearerContext(ies...) } // NewBearerContextWithinCreateIndirectDataForwardingTunnelResponse creates a new BearerContext used within CreateIndirectDataForwardingTunnelResponse. func NewBearerContextWithinCreateIndirectDataForwardingTunnelResponse(ebi, cause *IE, fTEIDs ...*IE) *IE { ies := []*IE{ebi, cause} ies = append(ies, fTEIDs...) return NewBearerContext(ies...) } // NewBearerContextWithinForwardRelocationRequest creates a new BearerContext used within ForwardRelocationRequest. func NewBearerContextWithinForwardRelocationRequest(ebi, tft, qos, container, ti, flags *IE, fTEIDs ...*IE) *IE { ies := []*IE{ebi, tft, qos, container, ti, flags} ies = append(ies, fTEIDs...) return NewBearerContext(ies...) } // NewBearerContextWithinContextResponse creates a new BearerContext used within ContextResponse. func NewBearerContextWithinContextResponse(ebi, tft, qos, container, ti *IE, fTEIDs ...*IE) *IE { ies := []*IE{ebi, tft, qos, container, ti} ies = append(ies, fTEIDs...) return NewBearerContext(ies...) } // NewBearerContextWithinContextAcknowledge creates a new BearerContext used within ContextAcknowledge. func NewBearerContextWithinContextAcknowledge(ebi, fwdFTEID *IE) *IE { return NewBearerContext(ebi, fwdFTEID) } // BearerContext returns the []*IE inside BearerContext IE. func (i *IE) BearerContext() ([]*IE, error) { if i.Type != BearerContext { return nil, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return nil, io.ErrUnexpectedEOF } ies, err := ParseMultiIEs(i.Payload) if err != nil { return nil, err } return ies, nil } ================================================ FILE: gtpv2/ie/bearer-flags.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewBearerFlags creates a new BearerFlags IE. func NewBearerFlags(asi, vInd, vb, ppc uint8) *IE { i := New(BearerFlags, 0x00, make([]byte, 1)) i.Payload[0] |= ((asi << 3 & 0x08) | (vInd << 2 & 0x04) | (vb << 1 & 0x2) | ppc&0x01) return i } // BearerFlags returns BearerFlags in uint8(=as it is) if the type of IE matches. func (i *IE) BearerFlags() (uint8, error) { switch i.Type { case BearerFlags: return i.ValueAsUint8() case BearerContext: ies, err := i.BearerContext() if err != nil { return 0, err } for _, child := range ies { if child.Type == BearerFlags { return child.BearerFlags() } } return 0, ErrIENotFound default: return 0, &InvalidTypeError{Type: i.Type} } } // MustBearerFlags returns BearerFlags in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustBearerFlags() uint8 { v, _ := i.BearerFlags() return v } // HasPPC reports whether an IE has PPC bit. func (i *IE) HasPPC() bool { v, err := i.BearerFlags() if err != nil { return false } return has1stBit(v) } // HasVB reports whether an IE has VB bit. func (i *IE) HasVB() bool { v, err := i.BearerFlags() if err != nil { return false } return has2ndBit(v) } // HasVind reports whether an IE has Vind bit. func (i *IE) HasVind() bool { v, err := i.BearerFlags() if err != nil { return false } return has3rdBit(v) } // HasASI reports whether an IE has ASI bit. func (i *IE) HasASI() bool { v, err := i.BearerFlags() if err != nil { return false } return has4thBit(v) } // ActivityStatusIndicator reports whether the bearer context is preserved in // the CN without corresponding Radio Access Bearer established. func (i *IE) ActivityStatusIndicator() bool { v, err := i.BearerFlags() if err != nil { return false } return v&0x08 == 1 } // VSRVCC reports whether this bearer is an IMS video bearer and is candidate // for PS-to-CS vSRVCC handover. func (i *IE) VSRVCC() bool { v, err := i.BearerFlags() if err != nil { return false } return v&0x04 == 1 } // VoiceBearer reports whether a voice bearer when doing PS-to-CS (v)SRVCC handover. func (i *IE) VoiceBearer() bool { v, err := i.BearerFlags() if err != nil { return false } return v&0x02 == 1 } // ProhibitPayloadCompression reports whether an SGSN should attempt to // compress the payload of user data when the users asks for it to be compressed. func (i *IE) ProhibitPayloadCompression() bool { v, err := i.BearerFlags() if err != nil { return false } return v&0x01 == 1 } ================================================ FILE: gtpv2/ie/bearer-qos.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "fmt" "io" "github.com/wmnsk/go-gtp/utils" ) // NewBearerQoS creates a new BearerQoS IE. func NewBearerQoS(pci, pl, pvi, qci uint8, umbr, dmbr, ugbr, dgbr uint64) *IE { v := NewBearerQoSFields(pci, pl, pvi, qci, umbr, dmbr, ugbr, dgbr) b, err := v.Marshal() if err != nil { return nil } return New(BearerQoS, 0x00, b) } // BearerQoS returns BearerQoS in BearerQoSFields type if the type of IE matches. func (i *IE) BearerQoS() (*BearerQoSFields, error) { switch i.Type { case BearerQoS: return ParseBearerQoSFields(i.Payload) case BearerContext: ies, err := i.BearerContext() if err != nil { return nil, fmt.Errorf("failed to retrieve BearerQoS: %w", err) } for _, child := range ies { if child.Type == BearerQoS { return child.BearerQoS() } } return nil, ErrIENotFound default: return nil, &InvalidTypeError{Type: i.Type} } } // BearerQoSFields is a set of fields in BearerQoS IE. type BearerQoSFields struct { ARP uint8 QCI uint8 MaximumBitRateForUplink uint64 // 40 bits MaximumBitRateForDownlink uint64 // 40 bits GuaranteedBitRateForUplink uint64 // 40 bits GuaranteedBitRateForDownlink uint64 // 40 bits } // NewBearerQoSFields creates a new BearerQoSFields. func NewBearerQoSFields(pci, pl, pvi, qci uint8, umbr, dmbr, ugbr, dgbr uint64) *BearerQoSFields { return &BearerQoSFields{ ARP: (pci << 6 & 0x40) | (pl << 2 & 0x3c) | (pvi & 0x01), QCI: qci, MaximumBitRateForUplink: umbr, MaximumBitRateForDownlink: dmbr, GuaranteedBitRateForUplink: ugbr, GuaranteedBitRateForDownlink: dgbr, } } // Marshal serializes BearerQoSFields. func (f *BearerQoSFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes BearerQoSFields. func (f *BearerQoSFields) MarshalTo(b []byte) error { if len(b) < 22 { return io.ErrUnexpectedEOF } b[0] = f.ARP b[1] = f.QCI copy(b[2:7], utils.Uint64To40(f.MaximumBitRateForUplink)) copy(b[7:12], utils.Uint64To40(f.MaximumBitRateForDownlink)) copy(b[12:17], utils.Uint64To40(f.GuaranteedBitRateForUplink)) copy(b[17:22], utils.Uint64To40(f.GuaranteedBitRateForDownlink)) return nil } // ParseBearerQoSFields decodes BearerQoSFields. func ParseBearerQoSFields(b []byte) (*BearerQoSFields, error) { f := &BearerQoSFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into BearerQoSFields. func (f *BearerQoSFields) UnmarshalBinary(b []byte) error { if len(b) < 22 { return io.ErrUnexpectedEOF } f.ARP = b[0] f.QCI = b[1] f.MaximumBitRateForUplink = utils.Uint40To64(b[2:7]) f.MaximumBitRateForDownlink = utils.Uint40To64(b[7:12]) f.GuaranteedBitRateForUplink = utils.Uint40To64(b[12:17]) f.GuaranteedBitRateForDownlink = utils.Uint40To64(b[17:22]) return nil } // MarshalLen returns the serial length of BearerQoSFields in int. func (f *BearerQoSFields) MarshalLen() int { return 22 } // QCILabel returns QCILabel in uint8 if the type of IE matches. func (i *IE) QCILabel() (uint8, error) { switch i.Type { case BearerQoS: if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return i.Payload[1], nil case FlowQoS: return i.ValueAsUint8() default: return 0, &InvalidTypeError{Type: i.Type} } } // MBRForUplink returns MBRForUplink in uint64 if the type of IE matches. func (i *IE) MBRForUplink() (uint64, error) { switch i.Type { case BearerQoS: if len(i.Payload) < 7 { return 0, io.ErrUnexpectedEOF } return utils.Uint40To64(i.Payload[2:7]), nil case FlowQoS: if len(i.Payload) < 6 { return 0, io.ErrUnexpectedEOF } return utils.Uint40To64(i.Payload[2:6]), nil default: return 0, io.ErrUnexpectedEOF } } // MustMBRForUplink returns MBRForUplink in uint64, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMBRForUplink() uint64 { v, _ := i.MBRForUplink() return v } // MBRForDownlink returns MBRForDownlink in uint64 if the type of IE matches. func (i *IE) MBRForDownlink() (uint64, error) { switch i.Type { case BearerQoS: if len(i.Payload) < 12 { return 0, io.ErrUnexpectedEOF } return utils.Uint40To64(i.Payload[7:12]), nil case FlowQoS: if len(i.Payload) < 11 { return 0, io.ErrUnexpectedEOF } return utils.Uint40To64(i.Payload[6:11]), nil default: return 0, io.ErrUnexpectedEOF } } // MustMBRForDownlink returns MBRForDownlink in uint64, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMBRForDownlink() uint64 { v, _ := i.MBRForDownlink() return v } // GBRForUplink returns GBRForUplink in uint64 if the type of IE matches. func (i *IE) GBRForUplink() (uint64, error) { switch i.Type { case BearerQoS: if len(i.Payload) < 17 { return 0, io.ErrUnexpectedEOF } return utils.Uint40To64(i.Payload[12:17]), nil case FlowQoS: if len(i.Payload) < 16 { return 0, io.ErrUnexpectedEOF } return utils.Uint40To64(i.Payload[11:16]), nil default: return 0, io.ErrUnexpectedEOF } } // MustGBRForUplink returns GBRForUplink in uint64, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustGBRForUplink() uint64 { v, _ := i.GBRForUplink() return v } // GBRForDownlink returns GBRForDownlink in uint64 if the type of IE matches. func (i *IE) GBRForDownlink() (uint64, error) { switch i.Type { case BearerQoS: if len(i.Payload) < 22 { return 0, io.ErrUnexpectedEOF } return utils.Uint40To64(i.Payload[17:22]), nil case FlowQoS: if len(i.Payload) < 21 { return 0, io.ErrUnexpectedEOF } return utils.Uint40To64(i.Payload[16:21]), nil default: return 0, io.ErrUnexpectedEOF } } // MustGBRForDownlink returns GBRForDownlink in uint64, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustGBRForDownlink() uint64 { v, _ := i.GBRForDownlink() return v } ================================================ FILE: gtpv2/ie/bearer-tft.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // TFT Operation Code definitions. const ( TFTOpIgnoreThisIE uint8 = 0 TFTOpCreateNewTFT uint8 = 1 TFTOpDeleteExistingTFT uint8 = 2 TFTOpAddPacketFiltersToExistingTFT uint8 = 3 TFTOpReplacePacketFiltersInExistingTFT uint8 = 4 TFTOpDeletePacketFiltersFromExistingTFT uint8 = 5 TFTOpNoTFTOperation uint8 = 6 ) // NewBearerTFT creates a new BearerTFT IE. // // Custom constructors for each operation code are available, which does not require // unnecessary parameters. func NewBearerTFT(op uint8, filters []*TFTPacketFilter, ids []uint8, params []*TFTParameter) *IE { v := NewTrafficFlowTemplate(op, filters, ids, params) b, err := v.Marshal() if err != nil { return nil } return New(BearerTFT, 0x00, b) } // NewBearerTFTCreateNewTFT creates a new BearerTFT IE with opcode=CreateNewTFT. func NewBearerTFTCreateNewTFT(filters []*TFTPacketFilter, params []*TFTParameter) *IE { return NewBearerTFT(TFTOpCreateNewTFT, filters, nil, params) } // NewBearerTFTAddPacketFilters creates a new BearerTFT IE with opcode=AddPacketFiltersToExistingTFT. func NewBearerTFTAddPacketFilters(filters []*TFTPacketFilter, params []*TFTParameter) *IE { return NewBearerTFT(TFTOpAddPacketFiltersToExistingTFT, filters, nil, params) } // NewBearerTFTReplacePacketFilters creates a new BearerTFT IE with opcode=ReplacePacketFiltersInExistingTFT. func NewBearerTFTReplacePacketFilters(filters []*TFTPacketFilter, params []*TFTParameter) *IE { return NewBearerTFT(TFTOpReplacePacketFiltersInExistingTFT, filters, nil, params) } // NewBearerTFTDeletePacketFilters creates a new BearerTFT IE with opcode=DeletePacketFiltersFromExistingTFT. func NewBearerTFTDeletePacketFilters(ids []uint8, params ...*TFTParameter) *IE { return NewBearerTFT(TFTOpDeletePacketFiltersFromExistingTFT, nil, ids, params) } // NewBearerTFTDeleteExistingTFT creates a new BearerTFT IE with opcode=DeleteExistingTFT. func NewBearerTFTDeleteExistingTFT(params ...*TFTParameter) *IE { return NewBearerTFT(TFTOpDeleteExistingTFT, nil, nil, params) } // NewBearerTFTNoTFTOperation creates a new BearerTFT IE with opcode=NoTFTOperation. func NewBearerTFTNoTFTOperation(params ...*TFTParameter) *IE { return NewBearerTFT(TFTOpNoTFTOperation, nil, nil, params) } // BearerTFT returns TrafficFlowTemplate struct if the type of IE matches. func (i *IE) BearerTFT() (*TrafficFlowTemplate, error) { return i.TrafficFlowTemplate() } ================================================ FILE: gtpv2/ie/cause.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "fmt" "io" ) // NewCause creates a new Cause IE. func NewCause(cause uint8, pce, bce, cs uint8, offendingIE *IE) *IE { i := New(Cause, 0x00, make([]byte, 2)) i.Payload[0] = cause i.Payload[1] = ((pce << 2) & 0x04) | ((bce << 1) & 0x02) | cs&0x01 if offendingIE != nil { // trailing zeroes are length, instance and spare fields which should be // filled with zeroes in this case (cf. §8.4, TS29.274) i.Payload = append(i.Payload, []byte{offendingIE.Type, 0x00, 0x00, 0x00}...) i.SetLength() } return i } // Cause returns Cause in uint8 if the type of IE matches. func (i *IE) Cause() (uint8, error) { switch i.Type { case Cause: return i.ValueAsUint8() case BearerContext: ies, err := i.BearerContext() if err != nil { return 0, fmt.Errorf("failed to retrieve Cause: %w", err) } for _, child := range ies { if child.Type == Cause { return child.Cause() } } return 0, ErrIENotFound default: return 0, &InvalidTypeError{Type: i.Type} } } // MustCause returns Cause in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustCause() uint8 { v, _ := i.Cause() return v } // CauseFlags returns CauseFlags in uint8 if the type of IE matches. func (i *IE) CauseFlags() (uint8, error) { switch i.Type { case Cause: if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return i.Payload[1], nil case BearerContext: ies, err := i.BearerContext() if err != nil { return 0, fmt.Errorf("failed to retrieve Cause: %w", err) } for _, child := range ies { if child.Type == Cause { return child.Cause() } } return 0, ErrIENotFound default: return 0, &InvalidTypeError{Type: i.Type} } } // MustCauseFlags returns CauseFlags in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustCauseFlags() uint8 { v, _ := i.CauseFlags() return v } // HasCS reports whether an IE has CS bit. func (i *IE) HasCS() bool { v, err := i.CauseFlags() if err != nil { return false } return has2ndBit(v) } // HasBCE reports whether an IE has BCE bit. func (i *IE) HasBCE() bool { v, err := i.CauseFlags() if err != nil { return false } return has1stBit(v) } // HasPCE reports whether an IE has PCE bit. func (i *IE) HasPCE() bool { v, err := i.CauseFlags() if err != nil { return false } return has3rdBit(v) } // IsRemoteCause returns IsRemoteCause in bool if the type of IE matches. func (i *IE) IsRemoteCause() bool { if i.Type != Cause { return false } if len(i.Payload) < 2 { return false } if i.Payload[1]>>2&0x01 == 1 { return true } return false } // IsBearerContextIEError returns IsBearerContextIEError in bool if the type of IE matches. func (i *IE) IsBearerContextIEError() bool { if i.Type != Cause { return false } if len(i.Payload) < 2 { return false } if i.Payload[1]>>1&0x01 == 1 { return true } return false } // IsPDNConnectionIEError returns IsPDNConnectionIEError in bool if the type of IE matches. func (i *IE) IsPDNConnectionIEError() bool { if i.Type != Cause { return false } if len(i.Payload) < 2 { return false } if i.Payload[1]&0x01 == 1 { return true } return false } // OffendingIE returns OffendingIE in *IE if the type of IE matches. // // Note that the returned IE has no payload (cf. §8.4, TS29.274). func (i *IE) OffendingIE() (*IE, error) { if i.Type != Cause { return nil, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 6 { return nil, io.ErrUnexpectedEOF } return Parse(i.Payload[2:6]) } // MustOffendingIE returns OffendingIE in *IE, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustOffendingIE() *IE { v, _ := i.OffendingIE() return v } ================================================ FILE: gtpv2/ie/charging-characteristics.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewChargingCharacteristics creates a new ChargingCharacteristics IE. func NewChargingCharacteristics(chr uint16) *IE { return NewUint16IE(ChargingCharacteristics, chr) } // ChargingCharacteristics returns the ChargingCharacteristics value in uint16 if the type of IE matches. func (i *IE) ChargingCharacteristics() (uint16, error) { if i.Type != ChargingCharacteristics { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint16() } // MustChargingCharacteristics returns ChargingCharacteristics in uint16, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustChargingCharacteristics() uint16 { v, _ := i.ChargingCharacteristics() return v } ================================================ FILE: gtpv2/ie/charging-id.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewChargingID creates a new ChargingID IE. func NewChargingID(id uint32) *IE { return NewUint32IE(ChargingID, id) } // ChargingID returns the ChargingID value in uint32 if the type of IE matches. func (i *IE) ChargingID() (uint32, error) { switch i.Type { case ChargingID: return i.ValueAsUint32() case BearerContext: ies, err := i.BearerContext() if err != nil { return 0, err } for _, child := range ies { if child.Type == ChargingID { return child.ChargingID() } } return 0, ErrIENotFound default: return 0, &InvalidTypeError{Type: i.Type} } } // MustChargingID returns ChargingID in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustChargingID() uint32 { v, _ := i.ChargingID() return v } ================================================ FILE: gtpv2/ie/cmi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "io" // NewCSGMembershipIndication creates a new CSGMembershipIndication IE. func NewCSGMembershipIndication(cmi uint8) *IE { return NewUint8IE(CSGMembershipIndication, cmi) } // CMI returns CMI in uint8 if the type of IE matches. func (i *IE) CMI() (uint8, error) { switch i.Type { case CSGMembershipIndication: return i.ValueAsUint8() case UserCSGInformation: if len(i.Payload) < 8 { return 0, io.ErrUnexpectedEOF } return i.Payload[7], nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustCMI returns CMI in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustCMI() uint8 { v, _ := i.CMI() return v } ================================================ FILE: gtpv2/ie/csg-id.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewCSGID creates a new CSGID IE. func NewCSGID(id uint32) *IE { return NewUint32IE(CSGID, id&0x7ffffff) } // CSGID returns CSGID in uint32 if the type of IE matches. func (i *IE) CSGID() (uint32, error) { switch i.Type { case CSGID: return i.ValueAsUint32() case UserCSGInformation: if len(i.Payload) < 7 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload[3:7]) & 0x7ffffff, nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustCSGID returns CSGID in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustCSGID() uint32 { v, _ := i.CSGID() return v } ================================================ FILE: gtpv2/ie/delay-value.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "time" ) // NewDelayValue creates a new DelayValue IE. func NewDelayValue(delay time.Duration) *IE { return NewUint8IE(DelayValue, uint8(delay.Seconds()*1000/50)) } // NewDelayValueRaw creates a new DelayValue IE from a uint8 value. // // The value should be in multiples of 50ms or zero. func NewDelayValueRaw(delay uint8) *IE { return NewUint8IE(DelayValue, delay) } // DelayValue returns DelayValue in time.Duration if the type of IE matches. // // The returned value is in time.Duration. To get the value in multiples of 50ms, // use ValueAsUint8 or access Payload field directly instead. func (i *IE) DelayValue() (time.Duration, error) { if i.Type != DelayValue { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return 0, io.ErrUnexpectedEOF } return time.Duration(i.Payload[0]/50) * time.Millisecond, nil } // MustDelayValue returns DelayValue in time.Duration, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustDelayValue() time.Duration { v, _ := i.DelayValue() return v } ================================================ FILE: gtpv2/ie/detach-type.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewDetachType creates a new DetachType IE. func NewDetachType(dtype uint8) *IE { return NewUint8IE(DetachType, dtype) } // DetachType returns DetachType in uint8 if the type of IE matches. func (i *IE) DetachType() (uint8, error) { if i.Type != DetachType { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustDetachType returns DetachType in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustDetachType() uint8 { v, _ := i.DetachType() return v } ================================================ FILE: gtpv2/ie/ebi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewEPSBearerID creates a new EPSBearerID IE. func NewEPSBearerID(ebi uint8) *IE { return NewUint8IE(EPSBearerID, ebi&0x0f) } // EPSBearerID returns EPSBearerID if the type of IE matches. func (i *IE) EPSBearerID() (uint8, error) { switch i.Type { case EPSBearerID: return i.ValueAsUint8() case BearerContext: ies, err := i.BearerContext() if err != nil { return 0, err } for _, child := range ies { if child.Type == EPSBearerID { return child.EPSBearerID() } } return 0, ErrIENotFound default: return 0, &InvalidTypeError{Type: i.Type} } } // MustEPSBearerID returns EPSBearerID in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustEPSBearerID() uint8 { v, _ := i.EPSBearerID() return v } ================================================ FILE: gtpv2/ie/epc-timer.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "errors" "io" "math" "time" ) // NewEPCTimer creates a new EPCTimer IE. func NewEPCTimer(duration time.Duration) *IE { // 8.87 EPC Timer // Timer unit // Bits 6 to 8 defines the timer value unit as follows: Bits // 8 7 6 // 0 0 0 value is incremented in multiples of 2 seconds // 0 0 1 value is incremented in multiples of 1 minute // 0 1 0 value is incremented in multiples of 10 minutes // 0 1 1 value is incremented in multiples of 1 hour // 1 0 0 value is incremented in multiples of 10 hours // 1 1 1 value indicates that the timer is infinite // // Other values shall be interpreted as multiples of 1 minute in this version of the protocol. // Timer unit and Timer value both set to all "zeros" shall be interpreted as an indication that the timer is stopped. var unit, value uint8 switch { case duration%(10*time.Hour) == 0: unit = 0x80 value = uint8(duration / (10 * time.Hour)) case duration%(1*time.Hour) == 0: unit = 0x60 value = uint8(duration / time.Hour) case duration%(10*time.Minute) == 0: unit = 0x40 value = uint8(duration / (10 * time.Minute)) case duration%(1*time.Minute) == 0: unit = 0x20 value = uint8(duration / time.Minute) case duration%(2*time.Second) == 0: unit = 0x00 value = uint8(duration / (2 * time.Second)) default: unit = 0xe0 value = 0 } return NewUint8IE(EPCTimer, unit+(value&0x1f)) } // NewEPCTimerRaw creates a new EPCTimer IE from a uint8 value. func NewEPCTimerRaw(duration uint8) *IE { return NewUint8IE(EPCTimer, duration) } // EPCTimer returns EPCTimer in time.Duration if the type of IE matches. // // The returned value is in time.Duration. To get the raw value as uint8, // use ValueAsUint8 or access Payload field directly instead. func (i *IE) EPCTimer() (time.Duration, error) { return i.Timer() } // MustEPCTimer returns EPCTimer in time.Duration, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustEPCTimer() time.Duration { v, _ := i.EPCTimer() return v } // Timer returns Timer in time.Duration if the type of IE matches. func (i *IE) Timer() (time.Duration, error) { if len(i.Payload) < 1 { return 0, io.ErrUnexpectedEOF } switch i.Type { case EPCTimer: var d time.Duration switch (i.Payload[0] & 0xe0) >> 5 { case 0x07: d = time.Duration(math.MaxInt64) case 0x04: d = time.Duration(i.Payload[0]&0x1f) * 10 * time.Hour case 0x03: d = time.Duration(i.Payload[0]&0x1f) * time.Hour case 0x02: d = time.Duration(i.Payload[0]&0x1f) * 10 * time.Minute case 0x01: d = time.Duration(i.Payload[0]&0x1f) * time.Minute case 0x00: d = time.Duration(i.Payload[0]&0x1f) * 2 * time.Second default: d = 0 } return d, nil case OverloadControlInformation: return 0, errors.New("not implemented") default: return 0, &InvalidTypeError{Type: i.Type} } } ================================================ FILE: gtpv2/ie/errors.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "errors" "fmt" ) // Error definitions. var ( ErrTooShortToParse = errors.New("too short to decode as GTP") ErrInvalidLength = errors.New("length value is invalid") ErrInvalidType = errors.New("invalid type") ErrIENotFound = errors.New("could not find the specified IE in a grouped IE") ErrIEValueNotFound = errors.New("could not find the specified value in an IE") ErrMalformed = errors.New("malformed IE") ) // InvalidTypeError indicates the type of IE is invalid. type InvalidTypeError struct { Type uint8 } // Error returns message with the invalid type given. func (e *InvalidTypeError) Error() string { return fmt.Sprintf("got invalid type: %v", e.Type) } ================================================ FILE: gtpv2/ie/f-teid.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "net" ) // NewFullyQualifiedTEID creates a new FullyQualifiedTEID IE. func NewFullyQualifiedTEID(ifType uint8, teid uint32, v4, v6 string) *IE { v := NewFullyQualifiedTEIDFields(ifType, teid, net.ParseIP(v4), net.ParseIP(v6)) b, err := v.Marshal() if err != nil { return nil } return New(FullyQualifiedTEID, 0x00, b) } // NewFullyQualifiedTEIDNetIP creates a new FullyQualifiedTEID IE from net.IP instead of string. func NewFullyQualifiedTEIDNetIP(ifType uint8, teid uint32, v4, v6 net.IP) *IE { v := NewFullyQualifiedTEIDFields(ifType, teid, v4, v6) b, err := v.Marshal() if err != nil { return nil } return New(FullyQualifiedTEID, 0x00, b) } // FullyQualifiedTEID returns FullyQualifiedTEID in FullyQualifiedTEIDFields type if the type of IE matches. func (i *IE) FullyQualifiedTEID() (*FullyQualifiedTEIDFields, error) { switch i.Type { case FullyQualifiedTEID: return ParseFullyQualifiedTEIDFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // FullyQualifiedTEIDFields is a set of fields in FullyQualifiedTEID IE. type FullyQualifiedTEIDFields struct { Flags uint8 // 7-8th bit, in the same octet as InterfaceType InterfaceType uint8 // 1-6-bit, in the same octet as Flags TEIDGREKey uint32 IPv4Address net.IP IPv6Address net.IP } // NewFullyQualifiedTEIDFields creates a new FullyQualifiedTEIDFields. func NewFullyQualifiedTEIDFields(ifType uint8, teid uint32, v4, v6 net.IP) *FullyQualifiedTEIDFields { f := &FullyQualifiedTEIDFields{ InterfaceType: ifType, TEIDGREKey: teid, } if v := v4.To4(); v != nil { f.Flags |= 0x80 f.IPv4Address = v } if v := v6.To16(); v != nil { f.Flags |= 0x40 f.IPv6Address = v } return f } // Marshal serializes FullyQualifiedTEIDFields. func (f *FullyQualifiedTEIDFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes FullyQualifiedTEIDFields. func (f *FullyQualifiedTEIDFields) MarshalTo(b []byte) error { l := len(b) if l < 5 { return io.ErrUnexpectedEOF } b[0] = f.InterfaceType binary.BigEndian.PutUint32(b[1:5], f.TEIDGREKey) offset := 5 if f.IPv4Address != nil { if l < offset+4 { return io.ErrUnexpectedEOF } if v := f.IPv4Address.To4(); v != nil { b[0] |= 0x80 copy(b[offset:offset+4], v) offset += 4 } } if f.IPv6Address != nil { if l < offset+16 { return io.ErrUnexpectedEOF } if v := f.IPv6Address.To16(); v != nil { b[0] |= 0x40 copy(b[offset:offset+16], v) } } return nil } // ParseFullyQualifiedTEIDFields decodes FullyQualifiedTEIDFields. func ParseFullyQualifiedTEIDFields(b []byte) (*FullyQualifiedTEIDFields, error) { f := &FullyQualifiedTEIDFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into FullyQualifiedTEIDFields. func (f *FullyQualifiedTEIDFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 5 { return io.ErrUnexpectedEOF } f.Flags = b[0] & 0xc0 f.InterfaceType = b[0] & 0x3f f.TEIDGREKey = binary.BigEndian.Uint32(b[1:5]) offset := 5 if has8thBit(f.Flags) { // has IPv4Address if l < offset+4 { return io.ErrUnexpectedEOF } if v := net.IP(b[offset : offset+4]).To4(); v != nil { f.IPv4Address = v offset += 4 } } if has7thBit(f.Flags) { // has IPv6Address if l < offset+16 { return io.ErrUnexpectedEOF } if v := net.IP(b[offset : offset+16]).To16(); v != nil { f.IPv6Address = v } } return nil } // MarshalLen returns the serial length of FullyQualifiedTEIDFields in int. func (f *FullyQualifiedTEIDFields) MarshalLen() int { l := 5 if has8thBit(f.Flags) { // has IPv4Address l += 4 } if has7thBit(f.Flags) { // has IPv6Address l += 16 } return l } // HasIPv4 reports whether an IE has IPv4 bit. func (i *IE) HasIPv4() bool { switch i.Type { case FullyQualifiedTEID: if len(i.Payload) < 1 { return false } return has8thBit(i.Payload[0]) default: return false } } // HasIPv6 reports whether an IE has IPv6 bit. func (i *IE) HasIPv6() bool { switch i.Type { case FullyQualifiedTEID: if len(i.Payload) < 1 { return false } return has7thBit(i.Payload[0]) default: return false } } // InterfaceType returns InterfaceType in uint8 if the type of IE matches. func (i *IE) InterfaceType() (uint8, error) { if i.Type != FullyQualifiedTEID { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return 0, io.ErrUnexpectedEOF } return i.Payload[0] & 0x3f, nil } // MustInterfaceType returns InterfaceType in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustInterfaceType() uint8 { v, _ := i.InterfaceType() return v } // GREKey returns GREKey in uint32 if the type of IE matches. func (i *IE) GREKey() (uint32, error) { if len(i.Payload) < 6 { return 0, io.ErrUnexpectedEOF } switch i.Type { case FullyQualifiedTEID: return binary.BigEndian.Uint32(i.Payload[1:5]), nil case S103PDNDataForwardingInfo: switch i.Payload[0] { case 4: if len(i.Payload) < 9 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload[5:9]), nil case 16: if len(i.Payload) < 21 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload[17:21]), nil default: return 0, ErrMalformed } default: return 0, &InvalidTypeError{Type: i.Type} } } // MustGREKey returns GREKey in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustGREKey() uint32 { v, _ := i.GREKey() return v } // TEID returns TEID in uint32 if the type of IE matches. func (i *IE) TEID() (uint32, error) { if len(i.Payload) < 5 { return 0, io.ErrUnexpectedEOF } switch i.Type { case FullyQualifiedTEID: return binary.BigEndian.Uint32(i.Payload[1:5]), nil case S1UDataForwarding: switch i.Payload[0] { case 4: if len(i.Payload) < 9 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload[5:9]), nil case 16: if len(i.Payload) < 21 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload[17:21]), nil default: return 0, ErrMalformed } default: return 0, &InvalidTypeError{Type: i.Type} } } // MustTEID returns TEID in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustTEID() uint32 { v, _ := i.TEID() return v } ================================================ FILE: gtpv2/ie/flow-qos.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewFlowQoS creates a new FlowQoS IE. func NewFlowQoS(qci uint8, umbr, dmbr, ugbr, dgbr uint64) *IE { v := NewFlowQoSFields(qci, umbr, dmbr, ugbr, dgbr) b, err := v.Marshal() if err != nil { return nil } return New(FlowQoS, 0x00, b) } // FlowQoS returns FlowQoS in FlowQoSFields type if the type of IE matches. func (i *IE) FlowQoS() (*FlowQoSFields, error) { switch i.Type { case FlowQoS: return ParseFlowQoSFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // FlowQoSFields is a set of fields in FlowQoS IE. type FlowQoSFields struct { QCI uint8 MaximumBitRateForUplink uint64 // 40 bits MaximumBitRateForDownlink uint64 // 40 bits GuaranteedBitRateForUplink uint64 // 40 bits GuaranteedBitRateForDownlink uint64 // 40 bits } // NewFlowQoSFields creates a new FlowQoSFields. func NewFlowQoSFields(qci uint8, umbr, dmbr, ugbr, dgbr uint64) *FlowQoSFields { return &FlowQoSFields{ QCI: qci, MaximumBitRateForUplink: umbr, MaximumBitRateForDownlink: dmbr, GuaranteedBitRateForUplink: ugbr, GuaranteedBitRateForDownlink: dgbr, } } // Marshal serializes FlowQoSFields. func (f *FlowQoSFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes FlowQoSFields. func (f *FlowQoSFields) MarshalTo(b []byte) error { if len(b) < 21 { return io.ErrUnexpectedEOF } b[0] = f.QCI copy(b[1:6], utils.Uint64To40(f.MaximumBitRateForUplink)) copy(b[6:11], utils.Uint64To40(f.MaximumBitRateForDownlink)) copy(b[11:16], utils.Uint64To40(f.GuaranteedBitRateForUplink)) copy(b[16:21], utils.Uint64To40(f.GuaranteedBitRateForDownlink)) return nil } // ParseFlowQoSFields decodes FlowQoSFields. func ParseFlowQoSFields(b []byte) (*FlowQoSFields, error) { f := &FlowQoSFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into FlowQoSFields. func (f *FlowQoSFields) UnmarshalBinary(b []byte) error { if len(b) < 21 { return io.ErrUnexpectedEOF } f.QCI = b[0] f.MaximumBitRateForUplink = utils.Uint40To64(b[1:6]) f.MaximumBitRateForDownlink = utils.Uint40To64(b[6:11]) f.GuaranteedBitRateForUplink = utils.Uint40To64(b[11:16]) f.GuaranteedBitRateForDownlink = utils.Uint40To64(b[16:21]) return nil } // MarshalLen returns the serial length of FlowQoSFields in int. func (f *FlowQoSFields) MarshalLen() int { return 21 } ================================================ FILE: gtpv2/ie/fq-csid.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "encoding/hex" "io" "net" ) // Node-ID Type definitions. const ( nodeIDIPv4 uint8 = iota nodeIDIPv6 nodeIDOther ) // NewFullyQualifiedCSID creates a new FullyQualifiedCSID IE. func NewFullyQualifiedCSID(nodeID string, csIDs ...uint16) *IE { v := NewFullyQualifiedCSIDFields(nodeID, csIDs...) b, err := v.Marshal() if err != nil { return nil } return New(FullyQualifiedCSID, 0x00, b) } // FullyQualifiedCSID returns FullyQualifiedCSID in FullyQualifiedCSIDFields type if the type of IE matches. func (i *IE) FullyQualifiedCSID() (*FullyQualifiedCSIDFields, error) { switch i.Type { case FullyQualifiedCSID: return ParseFullyQualifiedCSIDFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // FullyQualifiedCSIDFields is a set of fields in FullyQualifiedCSID IE. type FullyQualifiedCSIDFields struct { NodeIDType uint8 // 4-bit NumberOfCSIDs uint8 // 4-bit NodeID []byte CSIDs []uint16 } // NewFullyQualifiedCSIDFields creates a new FullyQualifiedCSIDFields. func NewFullyQualifiedCSIDFields(nodeID string, csIDs ...uint16) *FullyQualifiedCSIDFields { f := &FullyQualifiedCSIDFields{ NumberOfCSIDs: uint8(len(csIDs)), CSIDs: csIDs, } ip := net.ParseIP(nodeID) if ip == nil { var err error f.NodeID, err = hex.DecodeString(nodeID) if err != nil { return nil } f.NodeIDType = nodeIDOther } else if v4 := ip.To4(); v4 != nil { f.NodeID = v4 f.NodeIDType = nodeIDIPv4 } else { f.NodeID = ip f.NodeIDType = nodeIDIPv6 } return f } // Marshal serializes FullyQualifiedCSIDFields. func (f *FullyQualifiedCSIDFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes FullyQualifiedCSIDFields. func (f *FullyQualifiedCSIDFields) MarshalTo(b []byte) error { l := len(b) if l < 1 { return io.ErrUnexpectedEOF } b[0] = ((f.NodeIDType << 4) & 0xf0) | (f.NumberOfCSIDs & 0x0f) offset := 1 if l < offset+len(f.NodeID) { return io.ErrUnexpectedEOF } copy(b[offset:offset+len(f.NodeID)], f.NodeID) offset += len(f.NodeID) for n, csid := range f.CSIDs { if l < offset+n*2+2 { break } binary.BigEndian.PutUint16(b[offset+n*2:offset+n*2+2], csid) } return nil } // ParseFullyQualifiedCSIDFields decodes FullyQualifiedCSIDFields. func ParseFullyQualifiedCSIDFields(b []byte) (*FullyQualifiedCSIDFields, error) { f := &FullyQualifiedCSIDFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into FullyQualifiedCSIDFields. func (f *FullyQualifiedCSIDFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 1 { return io.ErrUnexpectedEOF } f.NodeIDType = (b[0] >> 4) & 0x0f f.NumberOfCSIDs = b[0] & 0x0f offset := 1 switch f.NodeIDType { case nodeIDIPv4, nodeIDOther: if l < offset+4 { return io.ErrUnexpectedEOF } f.NodeID = b[offset : offset+4] offset += 4 case nodeIDIPv6: if l < offset+16 { return io.ErrUnexpectedEOF } f.NodeID = b[offset : offset+16] offset += 16 default: return ErrMalformed } for { if l < offset+2 { break } f.CSIDs = append(f.CSIDs, binary.BigEndian.Uint16(b[offset:offset+2])) offset += 2 } return nil } // MarshalLen returns the serial length of FullyQualifiedCSIDFields in int. func (f *FullyQualifiedCSIDFields) MarshalLen() int { l := 1 switch f.NodeIDType { case nodeIDIPv4, nodeIDOther: l += 4 case nodeIDIPv6: l += 16 default: } l += len(f.CSIDs) * 2 return l } // NodeIDType returns NodeIDType in uint8 if the type of IE matches. func (i *IE) NodeIDType() (uint8, error) { if len(i.Payload) < 1 { return 0, io.ErrUnexpectedEOF } switch i.Type { case FullyQualifiedCSID: return (i.Payload[0] >> 4) & 0x0f, nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustNodeIDType returns NodeIDType in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustNodeIDType() uint8 { v, _ := i.NodeIDType() return v } // NodeID returns NodeID in []byte if the type of IE matches. func (i *IE) NodeID() ([]byte, error) { if len(i.Payload) < 1 { return nil, io.ErrUnexpectedEOF } switch i.Type { case FullyQualifiedCSID: switch (i.Payload[0] >> 4) & 0x0f { case nodeIDIPv4, nodeIDOther: if len(i.Payload) < 6 { return nil, io.ErrUnexpectedEOF } return i.Payload[1:5], nil case nodeIDIPv6: if len(i.Payload) < 18 { return nil, io.ErrUnexpectedEOF } return i.Payload[1:17], nil default: return nil, ErrMalformed } default: return nil, &InvalidTypeError{Type: i.Type} } } // MustNodeID returns NodeID in []byte, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustNodeID() []byte { v, _ := i.NodeID() return v } // CSIDs returns CSIDs in []uint16 if the type of IE matches. func (i *IE) CSIDs() ([]uint16, error) { if len(i.Payload) < 1 { return nil, io.ErrUnexpectedEOF } switch i.Type { case FullyQualifiedCSID: offset := 0 switch (i.Payload[0] >> 4) & 0x0f { case nodeIDIPv4, nodeIDOther: offset += 5 case nodeIDIPv6: offset += 17 default: return nil, ErrMalformed } var csids []uint16 for { if offset+2 > len(i.Payload) { break } csids = append(csids, binary.BigEndian.Uint16(i.Payload[offset:offset+2])) offset += 2 } return csids, nil default: return nil, &InvalidTypeError{Type: i.Type} } } // MustCSIDs returns CSIDs in []uint16, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustCSIDs() []uint16 { v, _ := i.CSIDs() return v } ================================================ FILE: gtpv2/ie/fqdn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewFullyQualifiedDomainName creates a new FullyQualifiedDomainName IE. func NewFullyQualifiedDomainName(fqdn string) *IE { return NewFQDNIE(FullyQualifiedDomainName, fqdn) } // FullyQualifiedDomainName returns FullyQualifiedDomainName in string if the type of IE matches. func (i *IE) FullyQualifiedDomainName() (string, error) { if i.Type != FullyQualifiedDomainName { return "", &InvalidTypeError{Type: i.Type} } return i.ValueAsFQDN() } // MustFullyQualifiedDomainName returns FullyQualifiedDomainName in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustFullyQualifiedDomainName() string { v, _ := i.FullyQualifiedDomainName() return v } ================================================ FILE: gtpv2/ie/global-cn-id.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "github.com/wmnsk/go-gtp/utils" ) // NewGlobalCNID creates a new GlobalCNID IE. func NewGlobalCNID(mcc, mnc string, cnid uint16) *IE { i := New(GlobalCNID, 0x00, make([]byte, 5)) plmn, err := utils.EncodePLMN(mcc, mnc) if err != nil { return nil } copy(i.Payload[0:3], plmn) cnid &= 0xfff binary.BigEndian.PutUint16(i.Payload[3:5], cnid) return i } // CNID returns CNID in uinte16 if the type of IE matches. func (i *IE) CNID() (uint16, error) { if len(i.Payload) < 5 { return 0, io.ErrUnexpectedEOF } switch i.Type { case GlobalCNID: return binary.BigEndian.Uint16(i.Payload[3:5]), nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustCNID returns CNID in uint16, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustCNID() uint16 { v, _ := i.CNID() return v } ================================================ FILE: gtpv2/ie/guti.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "github.com/wmnsk/go-gtp/utils" ) // NewGUTI creates a new GUTI IE. func NewGUTI(mcc, mnc string, groupID uint16, code uint8, mTMSI uint32) *IE { v := NewGUTIFields(mcc, mnc, groupID, code, mTMSI) b, err := v.Marshal() if err != nil { return nil } return New(GUTI, 0x00, b) } // GUTI returns GUTI in GUTIFields type if the type of IE matches. func (i *IE) GUTI() (*GUTIFields, error) { switch i.Type { case GUTI: return ParseGUTIFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // GUTIFields is a set of fields in GUTI IE. type GUTIFields struct { MCC, MNC string MMEGroupID uint16 MMECode uint8 MTMSI uint32 } // NewGUTIFields creates a new GUTIFields. func NewGUTIFields(mcc, mnc string, groupID uint16, code uint8, mTMSI uint32) *GUTIFields { return &GUTIFields{ MCC: mcc, MNC: mnc, MMEGroupID: groupID, MMECode: code, MTMSI: mTMSI, } } // Marshal serializes GUTIFields. func (f *GUTIFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes GUTIFields. func (f *GUTIFields) MarshalTo(b []byte) error { l := len(b) if l < 3 { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.MCC, f.MNC) if err != nil { return nil } copy(b[0:3], plmn) if l < 10 { return io.ErrUnexpectedEOF } binary.BigEndian.PutUint16(b[3:5], f.MMEGroupID) b[5] = f.MMECode binary.BigEndian.PutUint32(b[6:10], f.MTMSI) return nil } // ParseGUTIFields decodes GUTIFields. func ParseGUTIFields(b []byte) (*GUTIFields, error) { f := &GUTIFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into GUTIFields. func (f *GUTIFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 3 { return io.ErrUnexpectedEOF } var err error f.MCC, f.MNC, err = utils.DecodePLMN(b[1:3]) if err != nil { return err } if l < 10 { return io.ErrUnexpectedEOF } f.MMEGroupID = binary.BigEndian.Uint16(b[3:5]) f.MMECode = b[5] f.MTMSI = binary.BigEndian.Uint32(b[6:10]) return nil } // MarshalLen returns the serial length of GUTIFields in int. func (f *GUTIFields) MarshalLen() int { return 10 } // MMEGroupID returns MMEGroupID in uint16 if the type of IE matches. func (i *IE) MMEGroupID() (uint16, error) { switch i.Type { case GUTI: if len(i.Payload) < 5 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[3:5]), nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustMMEGroupID returns MMEGroupID in uint16, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMMEGroupID() uint16 { v, _ := i.MMEGroupID() return v } // MMECode returns MMECode in uint8 if the type of IE matches. func (i *IE) MMECode() (uint8, error) { switch i.Type { case GUTI: if len(i.Payload) < 6 { return 0, io.ErrUnexpectedEOF } return i.Payload[5], nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustMMECode returns MMECode in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMMECode() uint8 { v, _ := i.MMECode() return v } // MTMSI returns MTMSI in uint32 if the type of IE matches. func (i *IE) MTMSI() (uint32, error) { switch i.Type { case GUTI: if len(i.Payload) < 10 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload[6:10]), nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustMTMSI returns MTMSI in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMTMSI() uint32 { v, _ := i.MTMSI() return v } ================================================ FILE: gtpv2/ie/hop-counter.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewHopCounter creates a new HopCounter IE. func NewHopCounter(hop uint8) *IE { return NewUint8IE(HopCounter, hop) } // HopCounter returns HopCounter in uint8 if the type of IE matches. func (i *IE) HopCounter() (uint8, error) { if i.Type != HopCounter { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustHopCounter returns HopCounter in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustHopCounter() uint8 { v, _ := i.HopCounter() return v } ================================================ FILE: gtpv2/ie/ie.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. /* Package ie provides encoding/decoding feature of GTPv2 Information Elements. */ package ie import ( "encoding/binary" "fmt" "io" "github.com/wmnsk/go-gtp/utils" ) // IE definitions. const ( IMSI uint8 = 1 Cause uint8 = 2 Recovery uint8 = 3 STNSR uint8 = 51 AccessPointName uint8 = 71 AggregateMaximumBitRate uint8 = 72 EPSBearerID uint8 = 73 IPAddress uint8 = 74 MobileEquipmentIdentity uint8 = 75 MSISDN uint8 = 76 Indication uint8 = 77 ProtocolConfigurationOptions uint8 = 78 PDNAddressAllocation uint8 = 79 BearerQoS uint8 = 80 FlowQoS uint8 = 81 RATType uint8 = 82 ServingNetwork uint8 = 83 BearerTFT uint8 = 84 TrafficAggregateDescription uint8 = 85 UserLocationInformation uint8 = 86 FullyQualifiedTEID uint8 = 87 TMSI uint8 = 88 GlobalCNID uint8 = 89 S103PDNDataForwardingInfo uint8 = 90 S1UDataForwarding uint8 = 91 DelayValue uint8 = 92 BearerContext uint8 = 93 ChargingID uint8 = 94 ChargingCharacteristics uint8 = 95 TraceInformation uint8 = 96 BearerFlags uint8 = 97 PDNType uint8 = 99 ProcedureTransactionID uint8 = 100 MMContextGSMKeyAndTriplets uint8 = 103 MMContextUMTSKeyUsedCipherAndQuintuplets uint8 = 104 MMContextGSMKeyUsedCipherAndQuintuplets uint8 = 105 MMContextUMTSKeyAndQuintuplets uint8 = 106 MMContextEPSSecurityContextQuadrupletsAndQuintuplets uint8 = 107 MMContextUMTSKeyQuadrupletsAndQuintuplets uint8 = 108 PDNConnection uint8 = 109 PDUNumbers uint8 = 110 PacketTMSI uint8 = 111 PTMSISignature uint8 = 112 HopCounter uint8 = 113 UETimeZone uint8 = 114 TraceReference uint8 = 115 CompleteRequestMessage uint8 = 116 GUTI uint8 = 117 FContainer uint8 = 118 FCause uint8 = 119 PLMNID uint8 = 120 TargetIdentification uint8 = 121 PacketFlowID uint8 = 123 RABContext uint8 = 124 SourceRNCPDCPContextInfo uint8 = 125 PortNumber uint8 = 126 APNRestriction uint8 = 127 SelectionMode uint8 = 128 SourceIdentification uint8 = 129 ChangeReportingAction uint8 = 131 FullyQualifiedCSID uint8 = 132 ChannelNeeded uint8 = 133 EMLPPPriority uint8 = 134 NodeType uint8 = 135 FullyQualifiedDomainName uint8 = 136 TI uint8 = 137 MBMSSessionDuration uint8 = 138 MBMSServiceArea uint8 = 139 MBMSSessionIdentifier uint8 = 140 MBMSFlowIdentifier uint8 = 141 MBMSIPMulticastDistribution uint8 = 142 MBMSDistributionAcknowledge uint8 = 143 RFSPIndex uint8 = 144 UserCSGInformation uint8 = 145 CSGInformationReportingAction uint8 = 146 CSGID uint8 = 147 CSGMembershipIndication uint8 = 148 ServiceIndicator uint8 = 149 DetachType uint8 = 150 LocalDistinguishedName uint8 = 151 NodeFeatures uint8 = 152 MBMSTimeToDataTransfer uint8 = 153 Throttling uint8 = 154 AllocationRetensionPriority uint8 = 155 EPCTimer uint8 = 156 SignallingPriorityIndication uint8 = 157 TMGI uint8 = 158 AdditionalMMContextForSRVCC uint8 = 159 AdditionalFlagsForSRVCC uint8 = 160 MDTConfiguration uint8 = 162 AdditionalProtocolConfigurationOptions uint8 = 163 AbsoluteTimeofMBMSDataTransfer uint8 = 164 HeNBInformationReporting uint8 = 165 IPv4ConfigurationParameters uint8 = 166 ChangeToReportFlags uint8 = 167 ActionIndication uint8 = 168 TWANIdentifier uint8 = 169 ULITimestamp uint8 = 170 MBMSFlags uint8 = 171 RANNASCause uint8 = 172 CNOperatorSelectionEntity uint8 = 173 TrustedWLANModeIndication uint8 = 174 NodeNumber uint8 = 175 NodeIdentifier uint8 = 176 PresenceReportingAreaAction uint8 = 177 PresenceReportingAreaInformation uint8 = 178 TWANIdentifierTimestamp uint8 = 179 OverloadControlInformation uint8 = 180 LoadControlInformation uint8 = 181 Metric uint8 = 182 SequenceNumber uint8 = 183 APNAndRelativeCapacity uint8 = 184 WLANOffloadabilityIndication uint8 = 185 PagingAndServiceInformation uint8 = 186 IntegerNumber uint8 = 187 MillisecondTimeStamp uint8 = 188 MonitoringEventInformation uint8 = 189 ECGIList uint8 = 190 RemoteUEContext uint8 = 191 RemoteUserID uint8 = 192 RemoteUEIPinformation uint8 = 193 CIoTOptimizationsSupportIndication uint8 = 194 SCEFPDNConnection uint8 = 195 HeaderCompressionConfiguration uint8 = 196 ExtendedProtocolConfigurationOptions uint8 = 197 ServingPLMNRateControl uint8 = 198 Counter uint8 = 199 MappedUEUsageType uint8 = 200 SecondaryRATUsageDataReport uint8 = 201 UPFunctionSelectionIndicationFlags uint8 = 202 MaximumPacketLossRate uint8 = 203 APNRateControlStatus uint8 = 204 ExtendedTraceInformation uint8 = 205 MonitoringEventExtensionInformation uint8 = 206 AdditionalRRMPolicyIndex uint8 = 207 V2XContext uint8 = 208 PC5QoSParameters uint8 = 209 ServicesAuthorized uint8 = 210 BitRate uint8 = 211 PC5QoSFlow uint8 = 212 SGiPtPTunnelAddress uint8 = 213 SpecialIETypeForIETypeExtension uint8 = 254 PrivateExtension uint8 = 255 ) // IE is a GTPv2 Information Element. type IE struct { Type uint8 Length uint16 instance uint8 Payload []byte ChildIEs []*IE } // New creates new IE. func New(itype, ins uint8, data []byte) *IE { ie := &IE{ Type: itype, instance: ins & 0x0f, Payload: data, } ie.SetLength() return ie } // SetInstance sets the instance. func (i *IE) SetInstance(ins uint8) { i.instance = ins & 0x0f } // WithInstance sets the instance and returns IE. func (i *IE) WithInstance(ins uint8) *IE { i.instance = ins & 0x0f return i } // Instance returns instance value in uint8 func (i *IE) Instance() uint8 { return i.instance & 0x0f } // Marshal returns the byte sequence generated from an IE instance. func (i *IE) Marshal() ([]byte, error) { b := make([]byte, i.MarshalLen()) if err := i.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (i *IE) MarshalTo(b []byte) error { l := len(b) if l < 4 { return io.ErrUnexpectedEOF } b[0] = i.Type binary.BigEndian.PutUint16(b[1:3], i.Length) b[3] = i.instance if i.IsGrouped() { offset := 4 for _, ie := range i.ChildIEs { if l < offset+ie.MarshalLen() { break } if err := ie.MarshalTo(b[offset : offset+ie.MarshalLen()]); err != nil { return err } offset += ie.MarshalLen() } return nil } if l < i.MarshalLen() { return io.ErrUnexpectedEOF } copy(b[4:i.MarshalLen()], i.Payload) return nil } // Parse decodes given byte sequence as a GTPv2 Information Element. func Parse(b []byte) (*IE, error) { ie := &IE{} if err := ie.UnmarshalBinary(b); err != nil { return nil, err } return ie, nil } // UnmarshalBinary sets the values retrieved from byte sequence in GTPv2 IE. func (i *IE) UnmarshalBinary(b []byte) error { l := len(b) if l < 4 { return io.ErrUnexpectedEOF } i.Type = b[0] i.Length = binary.BigEndian.Uint16(b[1:3]) if int(i.Length) > l-4 { return ErrInvalidLength } i.instance = b[3] i.Payload = b[4 : 4+int(i.Length)] if i.IsGrouped() { var err error i.ChildIEs, err = ParseMultiIEs(i.Payload) if err != nil { return err } } return nil } // MarshalLen returns field length in integer. func (i *IE) MarshalLen() int { if i.IsGrouped() { l := 4 for _, ie := range i.ChildIEs { l += ie.MarshalLen() } return l } return 4 + len(i.Payload) } // SetLength sets the length in Length field. func (i *IE) SetLength() { if i.IsGrouped() { l := 0 for _, ie := range i.ChildIEs { l += ie.MarshalLen() } i.Length = uint16(l) } i.Length = uint16(len(i.Payload)) } // Name returns the name of IE in string. func (i *IE) Name() string { if n, ok := ieTypeNameMap[i.Type]; ok { return n } return "Undefined" } // String returns the GTPv2 IE values in human readable format. func (i *IE) String() string { if i == nil { return "nil" } return fmt.Sprintf("{%s: {Type: %d, Length: %d, Instance: %#x, Payload: %#v}}", i.Name(), i.Type, i.Length, i.Instance(), i.Payload, ) } // ParseMultiIEs decodes multiple IEs at a time. // This is easy and useful but slower than decoding one by one. // When you don't know the number of IEs, this is the only way to decode them. // See benchmarks in diameter_test.go for the detail. func ParseMultiIEs(b []byte) ([]*IE, error) { var ies []*IE for { if len(b) == 0 { break } i, err := Parse(b) if err != nil { return nil, err } ies = append(ies, i) b = b[i.MarshalLen():] } return ies, nil } // NewUint8IE creates a new IE with uint8 value. func NewUint8IE(t, v uint8) *IE { return New(t, 0x00, []byte{v}) } // NewUint16IE creates a new IE with uint16 value. func NewUint16IE(t uint8, v uint16) *IE { i := New(t, 0x00, make([]byte, 2)) binary.BigEndian.PutUint16(i.Payload, v) return i } // NewUint32IE creates a new IE with uint32 value. func NewUint32IE(t uint8, v uint32) *IE { i := New(t, 0x00, make([]byte, 4)) binary.BigEndian.PutUint32(i.Payload, v) return i } // NewUint64IE creates a new IE with uint64 value. func NewUint64IE(t uint8, v uint64) *IE { i := New(t, 0x00, make([]byte, 8)) binary.BigEndian.PutUint64(i.Payload, v) return i } // NewStringIE creates a new IE with string value. func NewStringIE(t uint8, v string) *IE { return New(t, 0x00, []byte(v)) } // NewFQDNIE creates a new IE with FQDN value. func NewFQDNIE(t uint8, v string) *IE { return New(t, 0x00, utils.EncodeFQDN(v)) } // ValueAsUint8 returns the value of IE as uint8. func (i *IE) ValueAsUint8() (uint8, error) { if i.IsGrouped() { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return 0, io.ErrUnexpectedEOF } return i.Payload[0], nil } // ValueAsUint16 returns the value of IE as uint16. func (i *IE) ValueAsUint16() (uint16, error) { if i.IsGrouped() { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[0:2]), nil } // ValueAsUint32 returns the value of IE as uint32. func (i *IE) ValueAsUint32() (uint32, error) { if i.IsGrouped() { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 4 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(i.Payload[0:4]), nil } // ValueAsUint64 returns the value of IE as uint64. func (i *IE) ValueAsUint64() (uint64, error) { if i.IsGrouped() { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 8 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint64(i.Payload[0:8]), nil } // ValueAsString returns the value of IE as string. func (i *IE) ValueAsString() (string, error) { if i.IsGrouped() { return "", &InvalidTypeError{Type: i.Type} } return string(i.Payload), nil } // ValueAsFQDN returns the value of IE as string, decoded as FQDN. func (i *IE) ValueAsFQDN() (string, error) { if i.IsGrouped() { return "", &InvalidTypeError{Type: i.Type} } return utils.DecodeFQDN(i.Payload), nil } var ieTypeNameMap = map[uint8]string{ 1: "IMSI", 2: "Cause", 3: "Recovery", 51: "STNSR", 71: "AccessPointName", 72: "AggregateMaximumBitRate", 73: "EPSBearerID", 74: "IPAddress", 75: "MobileEquipmentIdentity", 76: "MSISDN", 77: "Indication", 78: "ProtocolConfigurationOptions", 79: "PDNAddressAllocation", 80: "BearerQoS", 81: "FlowQoS", 82: "RATType", 83: "ServingNetwork", 84: "BearerTFT", 85: "TrafficAggregateDescription", 86: "UserLocationInformation", 87: "FullyQualifiedTEID", 88: "TMSI", 89: "GlobalCNID", 90: "S103PDNDataForwardingInfo", 91: "S1UDataForwarding", 92: "DelayValue", 93: "BearerContext", 94: "ChargingID", 95: "ChargingCharacteristics", 96: "TraceInformation", 97: "BearerFlags", 99: "PDNType", 100: "ProcedureTransactionID", 103: "MMContextGSMKeyAndTriplets", 104: "MMContextUMTSKeyUsedCipherAndQuintuplets", 105: "MMContextGSMKeyUsedCipherAndQuintuplets", 106: "MMContextUMTSKeyAndQuintuplets", 107: "MMContextEPSSecurityContextQuadrupletsAndQuintuplets", 108: "MMContextUMTSKeyQuadrupletsAndQuintuplets", 109: "PDNConnection", 110: "PDUNumbers", 111: "PacketTMSI", 112: "PTMSISignature", 113: "HopCounter", 114: "UETimeZone", 115: "TraceReference", 116: "CompleteRequestMessage", 117: "GUTI", 118: "FContainer", 119: "FCause", 121: "PLMNID", 122: "TargetIdentification", 123: "PacketFlowID", 124: "RABContext", 125: "SourceRNCPDCPContextInfo", 126: "PortNumber", 127: "APNRestriction", 128: "SelectionMode", 129: "SourceIdentification", 130: "Reserved", 131: "ChangeReportingAction", 132: "FullyQualifiedCSID", 133: "ChannelNeeded", 134: "EMLPPPriority", 135: "NodeType", 136: "FullyQualifiedDomainName", 137: "TI", 138: "MBMSSessionDuration", 139: "MBMSServiceArea", 140: "MBMSSessionIdentifier", 141: "MBMSFlowIdentifier", 142: "MBMSIPMulticastDistribution", 143: "MBMSDistributionAcknowledge", 144: "RFSPIndex", 145: "UserCSGInformation", 146: "CSGInformationReportingAction", 147: "CSGID", 148: "CSGMembershipIndication", 149: "ServiceIndicator", 150: "DetachType", 151: "LocalDistinguishedName", 152: "NodeFeatures", 153: "MBMSTimeToDataTransfer", 154: "Throttling", 155: "AllocationRetensionPriority", 156: "EPCTimer", 157: "SignallingPriorityIndication", 158: "TMGI", 159: "AdditionalMMContextForSRVCC", 160: "AdditionalFlagsForSRVCC", 162: "MDTConfiguration", 163: "AdditionalProtocolConfigurationOptions", 164: "AbsoluteTimeofMBMSDataTransfer", 165: "HeNBInformationReporting", 166: "IPv4ConfigurationParameters", 167: "ChangeToReportFlags", 168: "ActionIndication", 169: "TWANIdentifier", 170: "ULITimestamp", 171: "MBMSFlags", 172: "RANNASCause", 173: "CNOperatorSelectionEntity", 174: "TrustedWLANModeIndication", 175: "NodeNumber", 176: "NodeIdentifier", 177: "PresenceReportingAreaAction", 178: "PresenceReportingAreaInformation", 179: "TWANIdentifierTimestamp", 180: "OverloadControlInformation", 181: "LoadControlInformation", 182: "Metric", 183: "SequenceNumber", 184: "APNAndRelativeCapacity", 185: "WLANOffloadabilityIndication", 186: "PagingAndServiceInformation", 187: "IntegerNumber", 188: "MillisecondTimeStamp", 189: "MonitoringEventInformation", 190: "ECGIList", 191: "RemoteUEContext", 192: "RemoteUserID", 193: "RemoteUEIPinformation", 194: "CIoTOptimizationsSupportIndication", 195: "SCEFPDNConnection", 196: "HeaderCompressionConfiguration", 197: "ExtendedProtocolConfigurationOptions", 198: "ServingPLMNRateControl", 199: "Counter", 200: "MappedUEUsageType", 201: "SecondaryRATUsageDataReport", 202: "UPFunctionSelectionIndicationFlags", 203: "MaximumPacketLossRate", 204: "APNRateControlStatus", 205: "ExtendedTraceInformation", 206: "MonitoringEventExtensionInformation", 207: "AdditionalRRMPolicyIndex", 208: "V2XContext", 209: "PC5QoSParameters", 210: "ServicesAuthorized", 211: "BitRate", 212: "PC5QoSFlow", 213: "SGiPtPTunnelAddress", 254: "SpecialIETypeForIETypeExtension", 255: "PrivateExtension", } ================================================ FILE: gtpv2/ie/ie_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "log" // Serialize serializes IE into bytes. // // Deprecated: use IE.Marshal instead. func (i *IE) Serialize() ([]byte, error) { log.Println("IE.Serialize is deprecated. use IE.Marshal instead") return i.Marshal() } // SerializeTo serializes IE into bytes given as b. // // Deprecated: use IE.MarshalTo instead. func (i *IE) SerializeTo(b []byte) error { log.Println("IE.SerializeTo is deprecated. use IE.MarshalTo instead") return i.MarshalTo(b) } // Decode decodes bytes as IE. // // Deprecated: use Parse instead. func Decode(b []byte) (*IE, error) { log.Println("Decode is deprecated. use Parse instead") return Parse(b) } // DecodeFromBytes decodes bytes as IE. // // Deprecated: use IE.UnmarshalBinary instead. func (i *IE) DecodeFromBytes(b []byte) error { log.Println("IE.DecodeFromBytes is deprecated. use IE.UnmarshalBinary instead") return i.UnmarshalBinary(b) } // Len returns the actual length of IE. // // Deprecated: use IE.MarshalLen instead. func (i *IE) Len() int { log.Println("IE.Len is deprecated. use IE.MarshalLen instead") return i.MarshalLen() } ================================================ FILE: gtpv2/ie/ie_fuzz_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/ie" ) func FuzzParse(f *testing.F) { for _, c := range cases { f.Add(c.serialized) } testIE := func(t *testing.T, v *ie.IE) { // Generated with // grep -ho -e "Must.*()" * | sort | uniq | sed -e 's/^/v./' v.MustAccessMode() v.MustAccessPointName() v.MustAggregateMaximumBitRateDown() v.MustAggregateMaximumBitRateUp() v.MustAPNRestriction() v.MustBearerFlags() v.MustCause() v.MustCauseFlags() v.MustChargingCharacteristics() v.MustChargingID() v.MustCMI() v.MustCNID() v.MustCSGID() v.MustCSIDs() v.MustDaylightSaving() v.MustDelayValue() v.MustDetachType() v.MustEBIs() v.MustEnterpriseID() v.MustEPCTimer() v.MustEPSBearerID() v.MustFullyQualifiedDomainName() v.MustGBRForDownlink() v.MustGBRForUplink() v.MustGREKey() v.MustHopCounter() v.MustHSGWAddress() v.MustIMSI() v.MustIntegerNumber() v.MustInterfaceType() v.MustIP() v.MustIPAddress() v.MustIPv4() v.MustIPv6() v.MustLocalDistinguishedName() v.MustMBMSFlags() v.MustMBRForDownlink() v.MustMBRForUplink() v.MustMCC() v.MustMMECode() v.MustMMEGroupID() v.MustMNC() v.MustMobileEquipmentIdentity() v.MustMSISDN() v.MustMTMSI() v.MustNodeFeatures() v.MustNodeID() v.MustNodeIDType() v.MustNodeType() v.MustOffendingIE() v.MustPacketTMSI() v.MustPagingPolicyIndication() v.MustPDNType() v.MustPLMNID() v.MustPortNumber() v.MustPrivateExtension() v.MustProcedureTransactionID() v.MustProtocolConfigurationOptions() v.MustPTMSISignature() v.MustRATType() v.MustRecovery() v.MustRFSPIndex() v.MustSelectionMode() v.MustServiceIndicator() v.MustServingNetwork() v.MustSGWAddress() v.MustTEID() v.MustTimestamp() v.MustTimeZone() v.MustTMSI() v.MustTraceID() } f.Fuzz(func(t *testing.T, data []byte) { if v, err := ie.Parse(data); err == nil && v == nil { t.Errorf("nil without error") } else if err == nil { testIE(t, v) for _, cie := range v.ChildIEs { testIE(t, cie) } } }) } ================================================ FILE: gtpv2/ie/ie_grouped.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import "sync" // NewGroupedIE creates a new IE with the given IEs. // // The IEs with nil value will be ignored. func NewGroupedIE(itype uint8, ies ...*IE) *IE { i := New(itype, 0x00, make([]byte, 0)) for _, ie := range ies { if ie == nil { continue } serialized, err := ie.Marshal() if err != nil { return nil } i.Payload = append(i.Payload, serialized...) i.ChildIEs = append(i.ChildIEs, ie) } i.SetLength() return i } // We're using map to avoid iterating over a list. // The value `true` is not actually used. // TODO: consider using a slice with utils in slices package introduced in Go 1.21. var ( mu sync.RWMutex defaultGroupedIEMap = map[uint8]bool{ BearerContext: true, PDNConnection: true, OverloadControlInformation: true, LoadControlInformation: true, RemoteUEContext: true, SCEFPDNConnection: true, V2XContext: true, PC5QoSParameters: true, } isGroupedFun = func(t uint8) bool { mu.RLock() defer mu.RUnlock() _, ok := defaultGroupedIEMap[t] return ok } ) // SetIsGroupedFun sets a function to check if an IE is of grouped type or not. func SetIsGroupedFun(fun func(t uint8) bool) { mu.Lock() defer mu.Unlock() isGroupedFun = fun } // AddGroupedIEType adds IE type(s) to the defaultGroupedIEMap. // This is useful when you want to add new IE types to the defaultGroupedIEMap. // // See also SetIsGroupedFun(). func AddGroupedIEType(ts ...uint8) { mu.Lock() defer mu.Unlock() for _, t := range ts { defaultGroupedIEMap[t] = true } } // IsGrouped reports whether an IE is grouped type or not. // // By default, this package determines if an IE is grouped type or not by checking // if the IE type is in the defaultGroupedIEMap. // You can change this entire behavior by calling SetIsGroupedFun(), or you can add // new IE types to the defaultGroupedIEMap by calling AddGroupedIEType(). func (i *IE) IsGrouped() bool { return isGroupedFun(i.Type) } // Add adds variable number of IEs to a grouped IE and update length of it. // This does nothing if the type of the IE is not grouped (no errors). func (i *IE) Add(ies ...*IE) { if !i.IsGrouped() { return } for _, ie := range ies { if ie == nil { continue } i.ChildIEs = append(i.ChildIEs, ie) serialized, err := ie.Marshal() if err != nil { continue } i.Payload = append(i.Payload, serialized...) } i.SetLength() } // Remove removes an IE looked up by type and instance. func (i *IE) Remove(typ, instance uint8) { if !i.IsGrouped() { return } i.Payload = nil newChildren := make([]*IE, len(i.ChildIEs)) idx := 0 for _, ie := range i.ChildIEs { if ie.Type == typ && ie.Instance() == instance { newChildren = newChildren[:len(newChildren)-1] continue } newChildren[idx] = ie idx++ serialized, err := ie.Marshal() if err != nil { continue } i.Payload = append(i.Payload, serialized...) } i.ChildIEs = newChildren i.SetLength() } // FindByType returns IE looked up by type and instance. // // The program may be slower when calling this method multiple times // because this ranges over a ChildIEs each time it is called. func (i *IE) FindByType(typ, instance uint8) (*IE, error) { if !i.IsGrouped() { return nil, ErrInvalidType } for _, ie := range i.ChildIEs { if ie.Type == typ && ie.Instance() == instance { return ie, nil } } return nil, ErrIENotFound } ================================================ FILE: gtpv2/ie/ie_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie_test import ( "net" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" ) var ( mac1, _ = net.ParseMAC("12:34:56:78:90:01") mac2, _ = net.ParseMAC("12:34:56:78:90:02") ) var cases = []struct { description string structured *ie.IE serialized []byte }{ { "IMSI", ie.NewIMSI("123451234567890"), []byte{0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0}, }, { "Cause", ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), []byte{0x02, 0x00, 0x02, 0x00, 0x10, 0x00}, }, { "CauseIMSIIMEINotKnown", ie.NewCause(gtpv2.CauseIMSIIMEINotKnown, 1, 0, 0, ie.NewIMSI("")), []byte{0x02, 0x00, 0x06, 0x00, 0x60, 0x04, 0x01, 0x00, 0x00, 0x00}, }, { "Recovery", ie.NewRecovery(0xff), []byte{0x03, 0x00, 0x01, 0x00, 0xff}, }, { "AccessPointName", ie.NewAccessPointName("some.apn.example"), []byte{0x47, 0x00, 0x11, 0x00, 0x04, 0x73, 0x6f, 0x6d, 0x65, 0x03, 0x61, 0x70, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65}, }, { "AggregateMaximumBitRate", ie.NewAggregateMaximumBitRate(0x11111111, 0x22222222), []byte{0x48, 0x00, 0x08, 0x00, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22}, }, { "EPSBearerID", ie.NewEPSBearerID(0x05), []byte{0x49, 0x00, 0x01, 0x00, 0x05}, }, { "IPAddress/v4", ie.NewIPAddress("1.1.1.1"), []byte{0x4a, 0x00, 0x04, 0x00, 0x01, 0x01, 0x01, 0x01}, }, { "IPAddress/v6", ie.NewIPAddress("2001::1"), []byte{0x4a, 0x00, 0x10, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, }, { "MobileEquipmentIdentity", ie.NewMobileEquipmentIdentity("123450123456789"), []byte{0x4b, 0x00, 0x08, 0x00, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9}, }, { "MSISDN", ie.NewMSISDN("123450123456789"), []byte{0x4c, 0x00, 0x08, 0x00, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9}, }, { "Indication", ie.NewIndication( 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ), []byte{0x4d, 0x00, 0x09, 0x00, 0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40, 0xa0, 0x01}, }, { "IndicationFromBitSequence", ie.NewIndicationFromBitSequence("101000010000100000010101000100001000100010000001010000001010000000000001"), []byte{0x4d, 0x00, 0x09, 0x00, 0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40, 0xa0, 0x01}, }, { "IndicationFromOctets/Full", ie.NewIndicationFromOctets(0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40, 0xa0, 0x01), []byte{0x4d, 0x00, 0x09, 0x00, 0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40, 0xa0, 0x01}, }, { "IndicationFromOctets/Short", ie.NewIndicationFromOctets(0xa1, 0x08), []byte{0x4d, 0x00, 0x02, 0x00, 0xa1, 0x08}, }, { "ProtocolConfigurationOptions", ie.NewProtocolConfigurationOptions( gtpv2.ConfigProtocolPPPWithIP, // see pco-ppp_test.go for how to create these payload. ie.NewPCOContainer(gtpv2.ProtoIDIPCP, []byte{0x01, 0x00, 0x00, 0x10, 0x03, 0x06, 0x01, 0x01, 0x01, 0x01, 0x81, 0x06, 0x02, 0x02, 0x02, 0x02}), ie.NewPCOContainer(gtpv2.ProtoIDPAP, []byte{0x01, 0x00, 0x00, 0x0c, 0x03, 0x66, 0x6f, 0x6f, 0x03, 0x62, 0x61, 0x72}), ie.NewPCOContainer(gtpv2.ProtoIDCHAP, []byte{0x01, 0x00, 0x00, 0x0c, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x66, 0x6f, 0x6f}), ie.NewPCOContainer(gtpv2.ContIDMSSupportOfNetworkRequestedBearerControlIndicator, nil), ie.NewPCOContainer(gtpv2.ContIDIPAddressAllocationViaNASSignalling, nil), ie.NewPCOContainer(gtpv2.ContIDDNSServerIPv4AddressRequest, nil), ie.NewPCOContainer(gtpv2.ContIDIPv4LinkMTURequest, nil), ), []byte{ 0x4e, 0x00, 0x3e, 0x00, // Extension / ConfigurationProtocol 0x80, // IPCP 0x80, 0x21, 0x10, 0x01, 0x00, 0x00, 0x10, 0x03, 0x06, 0x01, 0x01, 0x01, 0x01, 0x81, 0x06, 0x02, 0x02, 0x02, 0x02, // PAP 0xc0, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x0c, 0x03, 0x66, 0x6f, 0x6f, 0x03, 0x62, 0x61, 0x72, // CHAP 0xc2, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x0c, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x66, 0x6f, 0x6f, // Bearer control indicator 0x00, 0x05, 0x00, // IP alloc via NAS 0x00, 0x0a, 0x00, // DNS server request 0x00, 0x0d, 0x00, // IPv4 link MTU request 0x00, 0x10, 0x00, }, }, { "PDNAddressAllocation/v4", ie.NewPDNAddressAllocation("1.1.1.1"), []byte{0x4f, 0x00, 0x05, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01}, }, { "PDNAddressAllocation/v6", ie.NewPDNAddressAllocationIPv6("2001::1", 64), []byte{0x4f, 0x00, 0x12, 0x00, 0x02, 0x40, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, }, { "PDNAddressAllocation/v4v6", ie.NewPDNAddressAllocationDual("1.1.1.1", "2001::1", 64), []byte{0x4f, 0x00, 0x16, 0x00, 0x03, 0x40, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01}, }, { "PDNAddressAllocation/NonIP", ie.NewPDNAddressAllocation(""), []byte{0x4f, 0x00, 0x01, 0x00, 0x04}, }, { "BearerQoS", ie.NewBearerQoS(1, 2, 1, 0xff, 0x1111111111, 0x2222222222, 0x1111111111, 0x2222222222), []byte{0x50, 0x00, 0x16, 0x00, 0x49, 0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22}, }, { "FlowQoS", ie.NewFlowQoS(0xff, 0x1111111111, 0x2222222222, 0x1111111111, 0x2222222222), []byte{0x51, 0x00, 0x15, 0x00, 0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22}, }, { "RATType", ie.NewRATType(gtpv2.RATTypeEUTRAN), []byte{0x52, 0x00, 0x01, 0x00, 0x06}, }, { "ServingNetwork/2-digit", ie.NewServingNetwork("123", "45"), []byte{0x53, 0x00, 0x03, 0x00, 0x21, 0xf3, 0x54}, }, { "ServingNetwork/3-digit", ie.NewServingNetwork("123", "456"), []byte{0x53, 0x00, 0x03, 0x00, 0x21, 0x63, 0x54}, }, { "BearerTFT/TFTOpCreateNewTFT/NoParams", ie.NewBearerTFTCreateNewTFT( []*ie.TFTPacketFilter{ ie.NewTFTPacketFilter( ie.TFTPFPreRel7TFTFilter, 1, 0, ie.NewTFTPFComponentIPv4RemoteAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv4LocalAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv6RemoteAddress( net.ParseIP("2001::1"), net.CIDRMask(64, 128), ), ie.NewTFTPFComponentIPv6RemoteAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ie.NewTFTPFComponentIPv6LocalAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ), ie.NewTFTPacketFilter( ie.TFTPFDownlinkOnly, 2, 0, ie.NewTFTPFComponentProtocolIdentifierNextHeader(1), ie.NewTFTPFComponentSingleLocalPort(2152), ie.NewTFTPFComponentSingleRemotePort(2123), ie.NewTFTPFComponentLocalPortRange(2123, 2152), ie.NewTFTPFComponentRemotePortRange(2152, 2123), ), ie.NewTFTPacketFilter( ie.TFTPFUplinkOnly, 3, 0, ie.NewTFTPFComponentSecurityParameterIndex(0xdeadbeef), ie.NewTFTPFComponentTypeOfServiceTrafficClass(1, 2), ie.NewTFTPFComponentFlowLabel(0x00011111), ie.NewTFTPFComponentDestinationMACAddress(mac1), ie.NewTFTPFComponentSourceMACAddress(mac2), ), ie.NewTFTPacketFilter( ie.TFTPFBidirectional, 4, 0, ie.NewTFTPFComponentDot1QCTAGVID(0x0111), ie.NewTFTPFComponentDot1QSTAGVID(0x0222), ie.NewTFTPFComponentDot1QCTAGPCPDEI(3), ie.NewTFTPFComponentDot1QSTAGPCPDEI(5), ie.NewTFTPFComponentEthertype(0x0800), ), }, nil, ), []byte{ 0x54, 0x00, 0x9d, 0x00, 0x24, 0x01, 0x00, 0x57, 0x10, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x11, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x23, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x12, 0x00, 0x12, 0x30, 0x01, 0x40, 0x08, 0x68, 0x50, 0x08, 0x4b, 0x41, 0x08, 0x4b, 0x08, 0x68, 0x51, 0x08, 0x68, 0x08, 0x4b, 0x23, 0x00, 0x1a, 0x60, 0xde, 0xad, 0xbe, 0xef, 0x70, 0x01, 0x02, 0x80, 0x01, 0x11, 0x11, 0x81, 0x12, 0x34, 0x56, 0x78, 0x90, 0x01, 0x82, 0x12, 0x34, 0x56, 0x78, 0x90, 0x02, 0x34, 0x00, 0x0d, 0x83, 0x01, 0x11, 0x84, 0x02, 0x22, 0x85, 0x03, 0x86, 0x05, 0x87, 0x08, 0x00, }, }, { "BearerTFT/TFTOpCreateNewTFT/NoParams", ie.NewBearerTFTCreateNewTFT( []*ie.TFTPacketFilter{ ie.NewTFTPacketFilter( ie.TFTPFPreRel7TFTFilter, 1, 0, ie.NewTFTPFComponentIPv4RemoteAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv4LocalAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv6RemoteAddress( net.ParseIP("2001::1"), net.CIDRMask(64, 128), ), ie.NewTFTPFComponentIPv6RemoteAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ie.NewTFTPFComponentIPv6LocalAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ), ie.NewTFTPacketFilter( ie.TFTPFDownlinkOnly, 2, 0, ie.NewTFTPFComponentProtocolIdentifierNextHeader(1), ie.NewTFTPFComponentSingleLocalPort(2152), ie.NewTFTPFComponentSingleRemotePort(2123), ie.NewTFTPFComponentLocalPortRange(2123, 2152), ie.NewTFTPFComponentRemotePortRange(2152, 2123), ), ie.NewTFTPacketFilter( ie.TFTPFUplinkOnly, 3, 0, ie.NewTFTPFComponentSecurityParameterIndex(0xdeadbeef), ie.NewTFTPFComponentTypeOfServiceTrafficClass(1, 2), ie.NewTFTPFComponentFlowLabel(0x00011111), ie.NewTFTPFComponentDestinationMACAddress(mac1), ie.NewTFTPFComponentSourceMACAddress(mac2), ), ie.NewTFTPacketFilter( ie.TFTPFBidirectional, 4, 0, ie.NewTFTPFComponentDot1QCTAGVID(0x0111), ie.NewTFTPFComponentDot1QSTAGVID(0x0222), ie.NewTFTPFComponentDot1QCTAGPCPDEI(3), ie.NewTFTPFComponentDot1QSTAGPCPDEI(5), ie.NewTFTPFComponentEthertype(0x0800), ), }, []*ie.TFTParameter{ ie.NewTFTParameter(ie.TFTParamIDAuthorizationToken, []byte{0xde, 0xad, 0xbe, 0xef}), ie.NewTFTParameter(ie.TFTParamIDFlowIdentifier, []byte{0x11, 0x11, 0x22, 0x22}), ie.NewTFTParameter(ie.TFTParamIDPacketFileterIdentifier, []byte{0x01, 0x02, 0x03, 0x04}), }, ), []byte{ 0x54, 0x00, 0xaf, 0x00, 0x34, 0x01, 0x00, 0x57, 0x10, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x11, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x23, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x12, 0x00, 0x12, 0x30, 0x01, 0x40, 0x08, 0x68, 0x50, 0x08, 0x4b, 0x41, 0x08, 0x4b, 0x08, 0x68, 0x51, 0x08, 0x68, 0x08, 0x4b, 0x23, 0x00, 0x1a, 0x60, 0xde, 0xad, 0xbe, 0xef, 0x70, 0x01, 0x02, 0x80, 0x01, 0x11, 0x11, 0x81, 0x12, 0x34, 0x56, 0x78, 0x90, 0x01, 0x82, 0x12, 0x34, 0x56, 0x78, 0x90, 0x02, 0x34, 0x00, 0x0d, 0x83, 0x01, 0x11, 0x84, 0x02, 0x22, 0x85, 0x03, 0x86, 0x05, 0x87, 0x08, 0x00, 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x02, 0x04, 0x11, 0x11, 0x22, 0x22, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, }, }, { "BearerTFT/TFTOpDeleteExistingTFT/NoParams", ie.NewBearerTFTDeleteExistingTFT(), []byte{0x54, 0x00, 0x01, 0x00, 0x40}, }, { "BearerTFT/TFTOpDeleteExistingTFT/NoParams", ie.NewBearerTFTDeleteExistingTFT(), []byte{0x54, 0x00, 0x01, 0x00, 0x40}, }, { "BearerTFT/TFTOpDeleteExistingTFT/NoParams", ie.NewBearerTFTDeleteExistingTFT(), []byte{0x54, 0x00, 0x01, 0x00, 0x40}, }, { "BearerTFT/TFTOpDeleteExistingTFT/NoParams", ie.NewBearerTFTDeleteExistingTFT(), []byte{0x54, 0x00, 0x01, 0x00, 0x40}, }, { "BearerTFT/TFTOpDeleteExistingTFT/WithParams", ie.NewBearerTFTDeleteExistingTFT( ie.NewTFTParameter(ie.TFTParamIDAuthorizationToken, []byte{0xde, 0xad, 0xbe, 0xef}), ie.NewTFTParameter(ie.TFTParamIDFlowIdentifier, []byte{0x11, 0x11, 0x22, 0x22}), ie.NewTFTParameter(ie.TFTParamIDPacketFileterIdentifier, []byte{0x01, 0x02, 0x03, 0x04}), ), []byte{ 0x54, 0x00, 0x13, 0x00, 0x50, 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x02, 0x04, 0x11, 0x11, 0x22, 0x22, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, }, }, { "BearerTFT/TFTOpAddPacketFilters/NoParams", ie.NewBearerTFTAddPacketFilters( []*ie.TFTPacketFilter{ ie.NewTFTPacketFilter( ie.TFTPFPreRel7TFTFilter, 1, 0, ie.NewTFTPFComponentIPv4RemoteAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ), }, nil, ), []byte{ 0x54, 0x00, 0x0d, 0x00, 0x61, 0x01, 0x00, 0x09, 0x10, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, }, }, { "BearerTFT/TFTOpReplacePacketFilters/NoParams", ie.NewBearerTFTReplacePacketFilters( []*ie.TFTPacketFilter{ ie.NewTFTPacketFilter( ie.TFTPFPreRel7TFTFilter, 1, 0, ie.NewTFTPFComponentIPv4RemoteAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ), }, nil, ), []byte{ 0x54, 0x00, 0x0d, 0x00, 0x81, 0x01, 0x00, 0x09, 0x10, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, }, }, { "BearerTFT/TFTOpDeletePacketFilters/NoParams", ie.NewBearerTFTDeletePacketFilters([]uint8{1, 2, 3, 4}, nil), []byte{0x54, 0x00, 0x05, 0x00, 0xa4, 0x01, 0x02, 0x03, 0x04}, }, { "BearerTFT/TFTOpDeletePacketFilters/WithParams", ie.NewBearerTFTDeletePacketFilters( []uint8{1, 2, 3, 4}, ie.NewTFTParameter(ie.TFTParamIDAuthorizationToken, []byte{0xde, 0xad, 0xbe, 0xef}), ie.NewTFTParameter(ie.TFTParamIDFlowIdentifier, []byte{0x11, 0x11, 0x22, 0x22}), ie.NewTFTParameter(ie.TFTParamIDPacketFileterIdentifier, []byte{0x01, 0x02, 0x03, 0x04}), ), []byte{ 0x54, 0x00, 0x17, 0x00, 0xb4, 0x01, 0x02, 0x03, 0x04, 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x02, 0x04, 0x11, 0x11, 0x22, 0x22, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, }, }, { "BearerTFT/TFTOpNoTFTOperation/NoParams", ie.NewBearerTFTNoTFTOperation(), []byte{0x54, 0x00, 0x01, 0x00, 0xc0}, }, { "UserLocationInformation/Lazy-1", ie.NewUserLocationInformationLazy( "123", "45", 0x1111, 0x2222, 0x3333, -1, 0x5555, 0x666666, -1, 0x22222222, ), []byte{ 0x56, 0x00, 0x26, 0x00, // Flags 0xbb, // CGI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x22, 0x22, // SAI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x33, 0x33, // TAI 0x21, 0xf3, 0x54, 0x55, 0x55, // ECGI 0x21, 0xf3, 0x54, 0x00, 0x06, 0x66, 0x66, // RAI 0x21, 0xf3, 0x54, 0x11, 0x11, // Extended Macro eNB ID 0x21, 0xf3, 0x54, 0x22, 0x22, 0x22, }, }, { "UserLocationInformation/Lazy-2", ie.NewUserLocationInformationLazy( "123", "45", 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x666666, 0x11111111, 0x22222222, ), []byte{ 0x56, 0x00, 0x33, 0x00, // Flags 0xff, // CGI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x22, 0x22, // SAI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x33, 0x33, // RAI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x44, 0x44, // TAI 0x21, 0xf3, 0x54, 0x55, 0x55, // ECGI 0x21, 0xf3, 0x54, 0x00, 0x06, 0x66, 0x66, // RAI 0x21, 0xf3, 0x54, 0x11, 0x11, // Macro eNB ID 0x21, 0xf3, 0x54, 0x11, 0x11, 0x11, // Extended Macro eNB ID 0x21, 0xf3, 0x54, 0x22, 0x22, 0x22, }, }, { "UserLocationInformation/Full", ie.NewUserLocationInformation( 1, 1, 1, 1, 1, 1, 1, 1, "123", "45", 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x666666, 0x11111111, 0x22222222, ), []byte{ 0x56, 0x00, 0x33, 0x00, // Flags 0xff, // CGI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x22, 0x22, // SAI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x33, 0x33, // RAI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x44, 0x44, // TAI 0x21, 0xf3, 0x54, 0x55, 0x55, // ECGI 0x21, 0xf3, 0x54, 0x00, 0x06, 0x66, 0x66, // RAI 0x21, 0xf3, 0x54, 0x11, 0x11, // Macro eNB ID 0x21, 0xf3, 0x54, 0x11, 0x11, 0x11, // Extended Macro eNB ID 0x21, 0xf3, 0x54, 0x22, 0x22, 0x22, }, }, { "UserLocationInformation/Full/v0.7.9+", ie.NewUserLocationInformationStruct( ie.NewCGI("123", "45", 0x1111, 0x2222), ie.NewSAI("123", "45", 0x1111, 0x3333), ie.NewRAI("123", "45", 0x1111, 0x4444), ie.NewTAI("123", "45", 0x5555), ie.NewECGI("123", "45", 0x66666666), ie.NewLAI("123", "45", 0x1111), ie.NewMENBI("123", "45", 0x11111111), ie.NewEMENBI("123", "45", 0x22222222), ), []byte{ 0x56, 0x00, 0x33, 0x00, // Flags 0xff, // CGI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x22, 0x22, // SAI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x33, 0x33, // RAI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x44, 0x44, // TAI 0x21, 0xf3, 0x54, 0x55, 0x55, // ECGI 0x21, 0xf3, 0x54, 0x06, 0x66, 0x66, 0x66, // RAI 0x21, 0xf3, 0x54, 0x11, 0x11, // Macro eNB ID 0x21, 0xf3, 0x54, 0x11, 0x11, 0x11, // Extended Macro eNB ID 0x21, 0xf3, 0x54, 0x22, 0x22, 0x22, }, }, { "FullyQualifiedTEID/v4", ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11MMEGTPC, 0xffffffff, "1.1.1.1", ""), []byte{0x57, 0x00, 0x09, 0x00, 0x8a, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01}, }, { "FullyQualifiedTEID/v6", ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11MMEGTPC, 0xffffffff, "", "2001::1"), []byte{0x57, 0x00, 0x15, 0x00, 0x4a, 0xff, 0xff, 0xff, 0xff, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, }, { "FullyQualifiedTEID/v4v6", ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11MMEGTPC, 0xffffffff, "1.1.1.1", "2001::1"), []byte{0x57, 0x00, 0x19, 0x00, 0xca, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, }, { "TMSI", ie.NewTMSI(0xffffffff), []byte{0x58, 0x00, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff}, }, { "GlobalCNID", ie.NewGlobalCNID("123", "45", 0xfff), []byte{0x59, 0x00, 0x05, 0x00, 0x21, 0xf3, 0x54, 0x0f, 0xff}, }, { "S103PDNDataForwardingInfo/v4", ie.NewS103PDNDataForwardingInfo("1.1.1.1", 0xdeadbeef, 5, 6, 7), []byte{0x5a, 0x00, 0x0d, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, 0xde, 0xad, 0xbe, 0xef, 0x03, 0x05, 0x06, 0x07}, }, { "S103PDNDataForwardingInfo/v6", ie.NewS103PDNDataForwardingInfo("2001::1", 0xdeadbeef, 5, 6, 7), []byte{0x5a, 0x00, 0x19, 0x00, 0x10, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, 0x03, 0x05, 0x06, 0x07}, }, { "S1UDataForwarding/v4", ie.NewS1UDataForwarding(5, "1.1.1.1", 0xdeadbeef), []byte{0x5b, 0x00, 0x0a, 0x00, 0x05, 0x04, 0x01, 0x01, 0x01, 0x01, 0xde, 0xad, 0xbe, 0xef}, }, { "S1UDataForwarding/v6", ie.NewS1UDataForwarding(5, "2001::1", 0xdeadbeef), []byte{0x5b, 0x00, 0x16, 0x00, 0x05, 0x10, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef}, }, { "DelayValue", ie.NewDelayValue(500 * time.Millisecond), []byte{0x5c, 0x00, 0x01, 0x00, 0x0a}, }, { "BearerContext", ie.NewBearerContext(ie.NewDelayValue(500*time.Millisecond), ie.NewDelayValue(100*time.Millisecond)), []byte{0x5d, 0x00, 0x0a, 0x00, 0x5c, 0x00, 0x01, 0x00, 0x0a, 0x5c, 0x00, 0x01, 0x00, 0x02}, }, { "ChargingID", ie.NewChargingID(0xffffffff), []byte{0x5e, 0x00, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff}, }, { "ChargingCharacteristics", ie.NewChargingCharacteristics(0xffff), []byte{0x5f, 0x00, 0x02, 0x00, 0xff, 0xff}, }, { "BearerFlags", ie.NewBearerFlags(1, 1, 1, 1), []byte{0x61, 0x00, 0x01, 0x00, 0x0f}, }, { "PDNType", ie.NewPDNType(gtpv2.PDNTypeIPv4), []byte{0x63, 0x00, 0x01, 0x00, 0x01}, }, { "ProcedureTransactionID", ie.NewProcedureTransactionID(1), []byte{0x64, 0x00, 0x01, 0x00, 0x01}, }, { "PacketTMSI", ie.NewPacketTMSI(0xdeadbeef), []byte{0x6f, 0x00, 0x04, 0x00, 0xde, 0xad, 0xbe, 0xef}, }, { "PTMSISignature", ie.NewPTMSISignature(0xbeebee), []byte{0x70, 0x00, 0x03, 0x00, 0xbe, 0xeb, 0xee}, }, { "HopCounter", ie.NewHopCounter(1), []byte{0x71, 0x00, 0x01, 0x00, 0x01}, }, { "UETimeZone", ie.NewUETimeZone(9*time.Hour, 0), []byte{0x72, 0x00, 0x02, 0x00, 0x63, 0x00}, }, { "UETimeZone", ie.NewUETimeZone(2*time.Hour, 0), []byte{0x72, 0x00, 0x02, 0x00, 0x80, 0x00}, }, { "TraceReference", ie.NewTraceReference("123", "45", 1), []byte{0x73, 0x00, 0x06, 0x00, 0x21, 0xf3, 0x54, 0x00, 0x00, 0x01}, }, { "GUTI", ie.NewGUTI("123", "45", 0x1111, 0x22, 0x33333333), []byte{0x75, 0x00, 0x0a, 0x00, 0x21, 0xf3, 0x54, 0x11, 0x11, 0x22, 0x33, 0x33, 0x33, 0x33}, }, { "PLMNID/2digits", ie.NewPLMNID("123", "45"), []byte{0x78, 0x00, 0x03, 0x00, 0x21, 0xf3, 0x54}, }, { "PLMNID/3digits", ie.NewPLMNID("123", "456"), []byte{0x78, 0x00, 0x03, 0x00, 0x21, 0x63, 0x54}, }, { "PortNumber", ie.NewPortNumber(2123), []byte{0x7e, 0x00, 0x02, 0x00, 0x08, 0x4b}, }, { "APNRestriction", ie.NewAPNRestriction(gtpv2.APNRestrictionPublic1), []byte{0x7f, 0x00, 0x01, 0x00, 0x01}, }, { "SelectionMode", ie.NewSelectionMode(gtpv2.SelectionModeMSProvidedAPNSubscriptionNotVerified), []byte{0x80, 0x00, 0x01, 0x00, 0x01}, }, { "FullyQualifiedCSID/v4", ie.NewFullyQualifiedCSID("1.1.1.1", 1), []byte{0x84, 0x00, 0x07, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01}, }, { "FullyQualifiedCSID/v4/multiCSIDs", ie.NewFullyQualifiedCSID("1.1.1.1", 1, 2), []byte{0x84, 0x00, 0x09, 0x00, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x02}, }, { "FullyQualifiedCSID/v6", ie.NewFullyQualifiedCSID("2001::1", 1), []byte{0x84, 0x00, 0x13, 0x00, 0x11, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01}, }, { "FullyQualifiedCSID/other", ie.NewFullyQualifiedCSID("12304501", 1), []byte{0x84, 0x00, 0x07, 0x00, 0x21, 0x12, 0x30, 0x45, 0x01, 0x00, 0x01}, }, { "NodeType", ie.NewNodeType(gtpv2.NodeTypeMME), []byte{0x87, 0x00, 0x01, 0x00, 0x01}, }, { "FullyQualifiedDomainName", ie.NewFullyQualifiedDomainName("some-fqdn.example"), []byte{0x88, 0x00, 0x12, 0x00, 0x09, 0x73, 0x6f, 0x6d, 0x65, 0x2d, 0x66, 0x71, 0x64, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65}, }, { "RFSPIndex", ie.NewRFSPIndex(1), []byte{0x90, 0x00, 0x01, 0x00, 0x01}, }, { "UserCSGInformation", ie.NewUserCSGInformation("123", "45", 0x00ffffff, gtpv2.AccessModeHybrid, 0, gtpv2.CMICSG), []byte{0x91, 0x00, 0x08, 0x00, 0x21, 0xf3, 0x54, 0x00, 0xff, 0xff, 0xff, 0x41}, }, { "CSGID", ie.NewCSGID(0x00ffffff), []byte{0x93, 0x00, 0x04, 0x00, 0x00, 0xff, 0xff, 0xff}, }, { "CSGMembershipIndication", ie.NewCSGMembershipIndication(gtpv2.CMICSG), []byte{0x94, 0x00, 0x01, 0x00, 0x01}, }, { "ServiceIndicator", ie.NewServiceIndicator(gtpv2.ServiceIndCSCall), []byte{0x95, 0x00, 0x01, 0x00, 0x01}, }, { "DetachType", ie.NewDetachType(gtpv2.DetachTypePS), []byte{0x96, 0x00, 0x01, 0x00, 0x01}, }, { "LocalDistinguishedName", ie.NewLocalDistinguishedName("some-name"), []byte{0x97, 0x00, 0x09, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x2d, 0x6e, 0x61, 0x6d, 0x65}, }, { "NodeFeatures", ie.NewNodeFeatures(0x01), []byte{0x98, 0x00, 0x01, 0x00, 0x01}, }, { "Throttling", ie.NewThrottling(20*time.Hour, 80), []byte{0x9a, 0x00, 0x02, 0x00, 0x82, 0x50}, }, { "AllocationRetensionPriority", ie.NewAllocationRetensionPriority(1, 2, 1), []byte{0x9b, 0x00, 0x01, 0x00, 0x49}, }, { "EPCTimer", ie.NewEPCTimer(20 * time.Hour), []byte{0x9c, 0x00, 0x01, 0x00, 0x82}, }, { "ULITimestamp", ie.NewULITimestamp(time.Date(2019, time.January, 1, 0, 0, 0, 0, time.UTC)), []byte{0xaa, 0x00, 0x04, 0x00, 0xdf, 0xd5, 0x2c, 0x00}, }, { "MBMSFlags", ie.NewMBMSFlags(1, 1), []byte{0xab, 0x00, 0x01, 0x00, 0x03}, }, { "RANNASCause", ie.NewRANNASCause(gtpv2.ProtoTypeS1APCause, gtpv2.CauseTypeNAS, []byte{0x01}), []byte{0xac, 0x00, 0x02, 0x00, 0x12, 0x01}, }, { "PagingAndServiceInformation", ie.NewPagingAndServiceInformation(5, 0x01, 0xff), []byte{0xba, 0x00, 0x03, 0x00, 0x05, 0x01, 0x7f}, }, { "IntegerNumber", ie.NewIntegerNumber(2020), []byte{0xbb, 0x00, 0x02, 0x00, 0x07, 0xe4}, }, { "PrivateExtension", ie.NewPrivateExtension(10415, []byte{0xde, 0xad, 0xbe, 0xef}), []byte{0xff, 0x00, 0x06, 0x00, 0x28, 0xaf, 0xde, 0xad, 0xbe, 0xef}, }, } func TestIEs(t *testing.T) { for _, c := range cases { t.Run("marshal/"+c.description, func(t *testing.T) { got, err := c.structured.Marshal() if err != nil { t.Fatal(err) } if diff := cmp.Diff(got, c.serialized); diff != "" { t.Error(diff) } }) t.Run("unmarshal/"+c.description, func(t *testing.T) { got, err := ie.Parse(c.serialized) if err != nil { t.Fatal(err) } opt := cmp.AllowUnexported(*got, *c.structured) if diff := cmp.Diff(got, c.structured, opt); diff != "" { t.Error(diff) } }) } } func TestIEAddRemove(t *testing.T) { i := ie.NewBearerContext( ie.NewIMSI("123451234567890").WithInstance(1), ie.NewMSISDN("819012345678"), ) i.Add(ie.NewAccessPointName("foo.example")) i.Add(nil) // ignored added := ie.NewBearerContext( ie.NewIMSI("123451234567890").WithInstance(1), ie.NewMSISDN("819012345678"), ie.NewAccessPointName("foo.example"), ) opt := cmp.AllowUnexported(*i, *added) if diff := cmp.Diff(i, added, opt); diff != "" { t.Error(diff) } i.Remove(ie.IMSI, 1) removed := ie.NewBearerContext( ie.NewMSISDN("819012345678"), ie.NewAccessPointName("foo.example"), ) opt = cmp.AllowUnexported(*i, *removed) if diff := cmp.Diff(i, removed, opt); diff != "" { t.Error(diff) } } ================================================ FILE: gtpv2/ie/imsi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewIMSI creates a new IMSI IE. func NewIMSI(imsi string) *IE { i, err := utils.StrToSwappedBytes(imsi, "f") if err != nil { return nil } return New(IMSI, 0x00, i) } // IMSI returns IMSI in string if the type of IE matches. func (i *IE) IMSI() (string, error) { if i.Type != IMSI { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return "", io.ErrUnexpectedEOF } return utils.SwappedBytesToStr(i.Payload, true), nil } // MustIMSI returns IMSI in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustIMSI() string { v, _ := i.IMSI() return v } ================================================ FILE: gtpv2/ie/indication.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "strconv" ) // TODO: add setters // TODO: consider using struct? // NewIndication creates a new Indication IE. // Note that each parameters should be 0 if false and 1 if true. Otherwise, // the value won't be set as expected in the bitwise operations. func NewIndication( daf, dtf, hi, dfi, oi, isrsi, israi, sgwci, sqci, uimsi, cfsi, crsi, ps, pt, si, msv, retLoc, pbic, srni, s6af, s4af, mbmdt, israu, ccrsi, cprai, arrl, ppoff, ppon, ppsi, csfbi, clii, cpsr, nsi, uasi, dtci, bdwi, psci, pcri, aosi, aopi, roaai, epcosi, cpopci, pmtmsi, s11tf, pnsi, unaccsi, wpmsi, fgsnn26, reprefi, fgsiwk, eevrsi, ltemui, ltempi, enbcrsi, tspcmi, csrmfi, mtedtn, mtedta, n5gnmi, fgcnrs, fgcnri, fsrhoi, ethpdn, sp98, sp97, sp96, sp95, sp94, sp93, sp92, emci uint8, ) *IE { i := New(Indication, 0x00, make([]byte, 9)) i.Payload[0] |= sgwci i.Payload[0] |= (israi << 1) i.Payload[0] |= (isrsi << 2) i.Payload[0] |= (oi << 3) i.Payload[0] |= (dfi << 4) i.Payload[0] |= (hi << 5) i.Payload[0] |= (dtf << 6) i.Payload[0] |= (daf << 7) i.Payload[1] |= msv i.Payload[1] |= (si << 1) i.Payload[1] |= (pt << 2) i.Payload[1] |= (ps << 3) i.Payload[1] |= (crsi << 4) i.Payload[1] |= (cfsi << 5) i.Payload[1] |= (uimsi << 6) i.Payload[1] |= (sqci << 7) i.Payload[2] |= ccrsi i.Payload[2] |= (israu << 1) i.Payload[2] |= (mbmdt << 2) i.Payload[2] |= (s4af << 3) i.Payload[2] |= (s6af << 4) i.Payload[2] |= (srni << 5) i.Payload[2] |= (pbic << 6) i.Payload[2] |= (retLoc << 7) i.Payload[3] |= cpsr i.Payload[3] |= (clii << 1) i.Payload[3] |= (csfbi << 2) i.Payload[3] |= (ppsi << 3) i.Payload[3] |= (ppon << 4) i.Payload[3] |= (ppoff << 5) i.Payload[3] |= (arrl << 6) i.Payload[3] |= (cprai << 7) i.Payload[4] |= aopi i.Payload[4] |= (aosi << 1) i.Payload[4] |= (pcri << 2) i.Payload[4] |= (psci << 3) i.Payload[4] |= (bdwi << 4) i.Payload[4] |= (dtci << 5) i.Payload[4] |= (uasi << 6) i.Payload[4] |= (nsi << 7) i.Payload[5] |= wpmsi i.Payload[5] |= (unaccsi << 1) i.Payload[5] |= (pnsi << 2) i.Payload[5] |= (s11tf << 3) i.Payload[5] |= (pmtmsi << 4) i.Payload[5] |= (cpopci << 5) i.Payload[5] |= (epcosi << 6) i.Payload[5] |= (roaai << 7) i.Payload[6] |= tspcmi i.Payload[6] |= (enbcrsi << 1) i.Payload[6] |= (ltempi << 2) i.Payload[6] |= (ltemui << 3) i.Payload[6] |= (eevrsi << 4) i.Payload[6] |= (fgsiwk << 5) i.Payload[6] |= (reprefi << 6) i.Payload[6] |= (fgsnn26 << 7) i.Payload[7] |= ethpdn i.Payload[7] |= (fsrhoi << 1) i.Payload[7] |= (fgcnri << 2) i.Payload[7] |= (fgcnrs << 3) i.Payload[7] |= (n5gnmi << 4) i.Payload[7] |= (mtedta << 5) i.Payload[7] |= (mtedtn << 6) i.Payload[7] |= (csrmfi << 7) i.Payload[8] |= emci i.Payload[8] |= (sp92 << 1) i.Payload[8] |= (sp93 << 2) i.Payload[8] |= (sp94 << 3) i.Payload[8] |= (sp95 << 4) i.Payload[8] |= (sp96 << 5) i.Payload[8] |= (sp97 << 6) i.Payload[8] |= (sp98 << 7) return i } // NewIndicationFromBitSequence creates a new Indication IE from string-formatted // sequence of bits. The sequence should look the same as Wireshark appearance. // The input is to be like "101000010000100000010101000100001000100010000001010000001010000000000001". func NewIndicationFromBitSequence(bitSequence string) *IE { if len(bitSequence) != 72 { return nil } ie := New(Indication, 0x00, make([]byte, 9)) for i, r := range bitSequence { bit, err := strconv.Atoi(string(r)) if err != nil { return nil } if bit > 1 { bit = 1 } // index: // 0 =< i < 8 : set bits in first octet // 8 =< i < 16: set bits in second octet ... // bit shift: // 0 =< i < 8 : shift i to left // 8 =< i < 16: shift i - 8 to left ... ie.Payload[i/8] |= uint8(bit << uint8(8*(i/8+1)-i-1)) } return ie } // NewIndicationFromOctets creates a new IndicationFromOctets IE from the set of octets. func NewIndicationFromOctets(octs ...uint8) *IE { ie := New(Indication, 0x00, make([]byte, 0)) for i, o := range octs { if i >= 9 { break } ie.Payload = append(ie.Payload, o) } ie.SetLength() return ie } // Indication returns Indication flags in []byte if the type of IE matches. func (i *IE) Indication() ([]byte, error) { if i.Type != Indication { return nil, &InvalidTypeError{i.Type} } return i.Payload, nil } // HasSGWCI reports whether an IE has SGWCI bit. func (i *IE) HasSGWCI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 1 { return false } return has1stBit(v[0]) } // HasISRAI reports whether an IE has ISRAI bit. func (i *IE) HasISRAI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 1 { return false } return has2ndBit(v[0]) } // HasISRSI reports whether an IE has ISRSI bit. func (i *IE) HasISRSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 1 { return false } return has3rdBit(v[0]) } // HasOI reports whether an IE has OI bit. func (i *IE) HasOI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 1 { return false } return has4thBit(v[0]) } // HasDFI reports whether an IE has DFI bit. func (i *IE) HasDFI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 1 { return false } return has5thBit(v[0]) } // HasHI reports whether an IE has HI bit. func (i *IE) HasHI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 1 { return false } return has6thBit(v[0]) } // HasDTF reports whether an IE has DTF bit. func (i *IE) HasDTF() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 1 { return false } return has7thBit(v[0]) } // HasDAF reports whether an IE has DAF bit. func (i *IE) HasDAF() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 1 { return false } return has8thBit(v[0]) } // HasMSV reports whether an IE has MSV bit. func (i *IE) HasMSV() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 2 { return false } return has1stBit(v[1]) } // HasSI reports whether an IE has SI bit. func (i *IE) HasSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 2 { return false } return has2ndBit(v[1]) } // HasPT reports whether an IE has PT bit. func (i *IE) HasPT() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 2 { return false } return has3rdBit(v[1]) } // HasPS reports whether an IE has PS bit. func (i *IE) HasPS() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 2 { return false } return has4thBit(v[1]) } // HasCRSI reports whether an IE has CRSI bit. func (i *IE) HasCRSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 2 { return false } return has5thBit(v[1]) } // HasCFSI reports whether an IE has CFSI bit. func (i *IE) HasCFSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 2 { return false } return has6thBit(v[1]) } // HasUIMSI reports whether an IE has UIMSI bit. func (i *IE) HasUIMSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 2 { return false } return has7thBit(v[1]) } // HasSQCI reports whether an IE has SQCI bit. func (i *IE) HasSQCI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 2 { return false } return has8thBit(v[1]) } // HasCCRSI reports whether an IE has CCRSI bit. func (i *IE) HasCCRSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 3 { return false } return has1stBit(v[2]) } // HasISRAU reports whether an IE has ISRAU bit. func (i *IE) HasISRAU() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 3 { return false } return has2ndBit(v[2]) } // HasMBMDT reports whether an IE has MBMDT bit. func (i *IE) HasMBMDT() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 3 { return false } return has3rdBit(v[2]) } // HasS4AF reports whether an IE has S4AF bit. func (i *IE) HasS4AF() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 3 { return false } return has4thBit(v[2]) } // HasS6AF reports whether an IE has S6AF bit. func (i *IE) HasS6AF() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 3 { return false } return has5thBit(v[2]) } // HasSRNI reports whether an IE has SRNI bit. func (i *IE) HasSRNI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 3 { return false } return has6thBit(v[2]) } // HasPBIC reports whether an IE has PBIC bit. func (i *IE) HasPBIC() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 3 { return false } return has7thBit(v[2]) } // HasRETLOC reports whether an IE has RETLOC bit. func (i *IE) HasRETLOC() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 3 { return false } return has8thBit(v[2]) } // HasCPSR reports whether an IE has CPSR bit. func (i *IE) HasCPSR() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 4 { return false } return has1stBit(v[3]) } // HasCLII reports whether an IE has CLII bit. func (i *IE) HasCLII() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 4 { return false } return has2ndBit(v[3]) } // HasCSFBI reports whether an IE has CSFBI bit. func (i *IE) HasCSFBI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 4 { return false } return has3rdBit(v[3]) } // HasPPSI reports whether an IE has PPSI bit. func (i *IE) HasPPSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 4 { return false } return has4thBit(v[3]) } // HasPPON reports whether an IE has PPON bit. func (i *IE) HasPPON() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 4 { return false } return has5thBit(v[3]) } // HasPPOF reports whether an IE has PPOF bit. func (i *IE) HasPPOF() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 4 { return false } return has6thBit(v[3]) } // HasARRL reports whether an IE has ARRL bit. func (i *IE) HasARRL() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 4 { return false } return has7thBit(v[3]) } // HasCPRAI reports whether an IE has CPRAI bit. func (i *IE) HasCPRAI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 4 { return false } return has8thBit(v[3]) } // HasAOPI reports whether an IE has AOPI bit. func (i *IE) HasAOPI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 5 { return false } return has1stBit(v[4]) } // HasAOSI reports whether an IE has AOSI bit. func (i *IE) HasAOSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 5 { return false } return has2ndBit(v[4]) } // HasPCRI reports whether an IE has PCRI bit. func (i *IE) HasPCRI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 5 { return false } return has3rdBit(v[4]) } // HasPSCI reports whether an IE has PSCI bit. func (i *IE) HasPSCI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 5 { return false } return has4thBit(v[4]) } // HasBDWI reports whether an IE has BDWI bit. func (i *IE) HasBDWI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 5 { return false } return has5thBit(v[4]) } // HasDTCI reports whether an IE has DTCI bit. func (i *IE) HasDTCI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 5 { return false } return has6thBit(v[4]) } // HasUASI reports whether an IE has UASI bit. func (i *IE) HasUASI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 5 { return false } return has7thBit(v[4]) } // HasNSI reports whether an IE has NSI bit. func (i *IE) HasNSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 5 { return false } return has8thBit(v[4]) } // HasWPMSI reports whether an IE has WPMSI bit. func (i *IE) HasWPMSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 6 { return false } return has1stBit(v[5]) } // HasUNACCSI reports whether an IE has UNACCSI bit. func (i *IE) HasUNACCSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 6 { return false } return has2ndBit(v[5]) } // HasPNSI reports whether an IE has PNSI bit. func (i *IE) HasPNSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 6 { return false } return has3rdBit(v[5]) } // HasS11TF reports whether an IE has S11TF bit. func (i *IE) HasS11TF() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 6 { return false } return has4thBit(v[5]) } // HasPMTMSI reports whether an IE has PMTMSI bit. func (i *IE) HasPMTMSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 6 { return false } return has5thBit(v[5]) } // HasCPOPCI reports whether an IE has CPOPCI bit. func (i *IE) HasCPOPCI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 6 { return false } return has6thBit(v[5]) } // HasEPCOSI reports whether an IE has EPCOSI bit. func (i *IE) HasEPCOSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 6 { return false } return has7thBit(v[5]) } // HasROAAI reports whether an IE has ROAAI bit. func (i *IE) HasROAAI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 6 { return false } return has8thBit(v[5]) } // HasTSPCMI reports whether an IE has TSPCMI bit. func (i *IE) HasTSPCMI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 7 { return false } return has1stBit(v[6]) } // HasENBCRSI reports whether an IE has ENBCRSI bit. func (i *IE) HasENBCRSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 7 { return false } return has2ndBit(v[6]) } // HasLTEMPI reports whether an IE has LTEMPI bit. func (i *IE) HasLTEMPI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 7 { return false } return has3rdBit(v[6]) } // HasLTEMUI reports whether an IE has LTEMUI bit. func (i *IE) HasLTEMUI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 7 { return false } return has4thBit(v[6]) } // HasEEVRSI reports whether an IE has EEVRSI bit. func (i *IE) HasEEVRSI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 7 { return false } return has5thBit(v[6]) } // Has5GSIWK reports whether an IE has 5GSIWK bit. func (i *IE) Has5GSIWK() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 7 { return false } return has6thBit(v[6]) } // HasREPREFI reports whether an IE has REPREFI bit. func (i *IE) HasREPREFI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 7 { return false } return has7thBit(v[6]) } // Has5GSNN26 reports whether an IE has 5GSNN26 bit. func (i *IE) Has5GSNN26() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 7 { return false } return has8thBit(v[6]) } // HasETHPDN reports whether an IE has ETHPDN bit. func (i *IE) HasETHPDN() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 8 { return false } return has1stBit(v[7]) } // Has5SRHOI reports whether an IE has 5SRHOI bit. func (i *IE) Has5SRHOI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 8 { return false } return has2ndBit(v[7]) } // Has5GCNRI reports whether an IE has 5GCNRI bit. func (i *IE) Has5GCNRI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 8 { return false } return has3rdBit(v[7]) } // Has5GCNRS reports whether an IE has 5GCNRS bit. func (i *IE) Has5GCNRS() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 8 { return false } return has4thBit(v[7]) } // HasN5GNMI reports whether an IE has N5GNMI bit. func (i *IE) HasN5GNMI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 8 { return false } return has5thBit(v[7]) } // HasMTEDTA reports whether an IE has MTEDTA bit. func (i *IE) HasMTEDTA() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 8 { return false } return has6thBit(v[7]) } // HasMTEDTN reports whether an IE has MTEDTN bit. func (i *IE) HasMTEDTN() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 8 { return false } return has7thBit(v[7]) } // HasCSRMFI reports whether an IE has CSRMFI bit. func (i *IE) HasCSRMFI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 8 { return false } return has8thBit(v[7]) } // HasEMCI reports whether an IE has EMCI bit. func (i *IE) HasEMCI() bool { v, err := i.Indication() if err != nil { return false } if len(v) < 9 { return false } return has1stBit(v[8]) } ================================================ FILE: gtpv2/ie/integer-number.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewIntegerNumber creates a new IntegerNumber IE. func NewIntegerNumber(num uint16) *IE { return NewUint16IE(IntegerNumber, num) } // IntegerNumber returns IntegerNumber in uint16 if the type of IE matches. func (i *IE) IntegerNumber() (uint16, error) { switch i.Type { case IntegerNumber: return i.ValueAsUint16() default: return 0, &InvalidTypeError{Type: i.Type} } } // MustIntegerNumber returns IntegerNumber in uint16, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustIntegerNumber() uint16 { v, _ := i.IntegerNumber() return v } ================================================ FILE: gtpv2/ie/ip-addr.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "net" ) // NewIPAddress creates a new IPAddress IE from string. func NewIPAddress(addr string) *IE { ip := net.ParseIP(addr) v4 := ip.To4() // IPv4 if v4 != nil { return New(IPAddress, 0x00, v4) } // IPv6 return New(IPAddress, 0x00, ip) } // NewIPAddressNetIP creates a new IPAddress IE from net.IP. func NewIPAddressNetIP(ip net.IP) *IE { if v := ip.To4(); v != nil { return New(IPAddress, 0x00, v) } if v := ip.To16(); v != nil { return New(IPAddress, 0x00, v) } // return IE w/ "something" anyway, to avoid crash return New(IPAddress, 0x00, ip) } // IPAddress returns IPAddress value if the type of IE matches. func (i *IE) IPAddress() (string, error) { ip, err := i.IP() if err != nil { return "", err } return ip.String(), nil } // MustIPAddress returns IPAddress in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustIPAddress() string { v, _ := i.IPAddress() return v } // IP returns IP value in net.IP if the type of IE matches. // // This does not validate the values. Even if the IP address fields in an // IE contains some invalid byte sequence, it just try to return as it is. // To retrieve only if it's valid, use IPv4 or IPv6 instead. // // If the IE has both IPv4 and IPv6, this returns IPv4. To retrieve IPv6 // value in that case, use IPv6() instead. func (i *IE) IP() (net.IP, error) { if len(i.Payload) < 4 { return nil, io.ErrUnexpectedEOF } switch i.Type { case IPAddress: return net.IP(i.Payload), nil case PDNAddressAllocation: if len(i.Payload) < 5 { return nil, io.ErrUnexpectedEOF } paa, err := ParsePDNAddressAllocationFields(i.Payload) if err != nil { return nil, err } switch paa.PDNType { case pdnTypeIPv4, pdnTypeIPv4v6: return paa.IPv4Address, nil case pdnTypeIPv6: return paa.IPv6Address, nil default: return nil, ErrIEValueNotFound } case S103PDNDataForwardingInfo: switch i.Payload[0] { case 4: if len(i.Payload) < 5 { return nil, io.ErrUnexpectedEOF } return net.IP(i.Payload[1:5]), nil case 16: if len(i.Payload) < 17 { return nil, io.ErrUnexpectedEOF } return net.IP(i.Payload[1:17]), nil default: return nil, ErrMalformed } case S1UDataForwarding: switch i.Payload[1] { case 4: if len(i.Payload) < 6 { return nil, io.ErrUnexpectedEOF } return net.IP(i.Payload[2:6]), nil case 16: if len(i.Payload) < 18 { return nil, io.ErrUnexpectedEOF } return net.IP(i.Payload[2:18]), nil default: return nil, ErrMalformed } case FullyQualifiedTEID: fteid, err := ParseFullyQualifiedTEIDFields(i.Payload) if err != nil { return nil, err } if i.HasIPv4() { return fteid.IPv4Address, nil } else if i.HasIPv6() { return fteid.IPv6Address, nil } return nil, ErrIEValueNotFound default: return nil, &InvalidTypeError{Type: i.Type} } } // MustIP returns IP in net.IP, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustIP() net.IP { v, _ := i.IP() return v } // IPv4 returns IPv4 value in net.IP if the type of IE matches. func (i *IE) IPv4() (net.IP, error) { ip, err := i.IP() if err != nil { return nil, err } if v := ip.To4(); v != nil { return v, nil } return nil, ErrIEValueNotFound } // MustIPv4 returns IPv4 in net.IP, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustIPv4() net.IP { v, _ := i.IPv4() return v } // IPv6 returns IPv6 value in net.IP if the type of IE matches. func (i *IE) IPv6() (net.IP, error) { if len(i.Payload) < 4 { return nil, io.ErrUnexpectedEOF } switch i.Type { case IPAddress, S103PDNDataForwardingInfo, S1UDataForwarding: ip, err := i.IP() if err != nil { return nil, err } if v := ip.To16(); v != nil { return v, nil } return nil, ErrIEValueNotFound case PDNAddressAllocation: paa, err := ParsePDNAddressAllocationFields(i.Payload) if err != nil { return nil, err } if v := paa.IPv6Address.To16(); v != nil { return v, nil } return nil, ErrIEValueNotFound case FullyQualifiedTEID: fteid, err := ParseFullyQualifiedTEIDFields(i.Payload) if err != nil { return nil, err } if i.HasIPv6() { if v := fteid.IPv6Address.To16(); v != nil { return v, nil } return nil, ErrIEValueNotFound } return nil, ErrIEValueNotFound default: return nil, &InvalidTypeError{Type: i.Type} } } // MustIPv6 returns IPv6 in net.IP, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustIPv6() net.IP { v, _ := i.IPv6() return v } ================================================ FILE: gtpv2/ie/ip-addr_test.go ================================================ package ie_test import ( "io" "net" "testing" "github.com/google/go-cmp/cmp" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" ) func TestPDNAddressAllocationIP(t *testing.T) { cases := []struct { description string paa *ie.IE pdnType uint8 ipv4 net.IP ipv6 net.IP }{ { "PDNType IPv4", ie.NewPDNAddressAllocation("1.2.3.4"), gtpv2.PDNTypeIPv4, net.ParseIP("1.2.3.4"), nil, }, { "PDNType IPv6", ie.NewPDNAddressAllocation("::1"), gtpv2.PDNTypeIPv6, nil, net.ParseIP("::1"), }, { "PDNType IPv4v6", ie.NewPDNAddressAllocationDual("1.2.3.4", "::1", 64), gtpv2.PDNTypeIPv4v6, net.ParseIP("1.2.3.4"), net.ParseIP("::1"), }, { "PDNType NonIP", ie.NewPDNAddressAllocation(""), gtpv2.PDNTypeNonIP, nil, nil, }, } for _, c := range cases { t.Run(c.description, func(t *testing.T) { pdnType := c.paa.MustPDNType() if diff := cmp.Diff(pdnType, c.pdnType); diff != "" { t.Error(diff) } ipv4, _ := c.paa.IPv4() if diff := cmp.Diff(ipv4, c.ipv4); diff != "" { t.Error(diff) } ipv6, _ := c.paa.IPv6() if diff := cmp.Diff(ipv6, c.ipv6); diff != "" { t.Error(diff) } ip, err := c.paa.IP() if err == nil { v := ipv4 if pdnType == gtpv2.PDNTypeIPv6 { v = ipv6 } if diff := cmp.Diff(ip, v); diff != "" { t.Error(diff) } } else if err != io.ErrUnexpectedEOF { t.Error(err) } }) } } ================================================ FILE: gtpv2/ie/ldn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewLocalDistinguishedName creates a new LocalDistinguishedName IE. func NewLocalDistinguishedName(name string) *IE { return NewStringIE(LocalDistinguishedName, name) } // LocalDistinguishedName returns LocalDistinguishedName in string if the type of IE matches. func (i *IE) LocalDistinguishedName() (string, error) { if i.Type != LocalDistinguishedName { return "", &InvalidTypeError{Type: i.Type} } return i.ValueAsString() } // MustLocalDistinguishedName returns LocalDistinguishedName in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustLocalDistinguishedName() string { v, _ := i.LocalDistinguishedName() return v } ================================================ FILE: gtpv2/ie/mbms-flags.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewMBMSFlags creates a new MBMSFlags IE. func NewMBMSFlags(lmri, msri uint8) *IE { i := New(MBMSFlags, 0x00, make([]byte, 1)) i.Payload[0] |= (lmri << 1 & 0x02) | (msri & 0x01) return i } // MBMSFlags returns MBMSFlags in uint8 if the type of IE matches. func (i *IE) MBMSFlags() (uint8, error) { if i.Type != MBMSFlags { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustMBMSFlags returns MBMSFlags in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMBMSFlags() uint8 { v, _ := i.MBMSFlags() return v } // HasMSRI reports whether an IE has MSRI bit. func (i *IE) HasMSRI() bool { v, err := i.MBMSFlags() if err != nil { return false } return has1stBit(v) } // HasLMRI reports whether an IE has LMRI bit. func (i *IE) HasLMRI() bool { v, err := i.MBMSFlags() if err != nil { return false } return has2ndBit(v) } // LocalMBMSBearerContextRelease reports whether the MBMS Session Stop Request // message is used to release the MBMS Bearer Context locally in the MME/SGSN. func (i *IE) LocalMBMSBearerContextRelease() bool { v, err := i.MBMSFlags() if err != nil { return false } return v&0x02 == 1 } // MBMSSessionReEstablishment reports whether the MBMS Session Start Request // message is used to re-establish an MBMS session. func (i *IE) MBMSSessionReEstablishment() bool { v, err := i.MBMSFlags() if err != nil { return false } return v&0x01 == 1 } ================================================ FILE: gtpv2/ie/mcc-mnc.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "errors" "io" "github.com/wmnsk/go-gtp/utils" ) // MCC returns MCC in string if the type of IE matches. func (i *IE) MCC() (string, error) { if len(i.Payload) < 3 { return "", io.ErrUnexpectedEOF } switch i.Type { case ServingNetwork, PLMNID: mcc, _, err := utils.DecodePLMN(i.Payload) if err != nil { return "", err } return mcc, nil case GlobalCNID, TraceReference, GUTI, UserCSGInformation: mcc, _, err := utils.DecodePLMN(i.Payload[:3]) if err != nil { return "", err } return mcc, nil case UserLocationInformation: uliFields, err := i.UserLocationInformation() if err != nil { return "", err } switch { case uliFields.HasCGI(): return uliFields.CGI.MCCFromPLMN(), nil case uliFields.HasSAI(): return uliFields.SAI.MCCFromPLMN(), nil case uliFields.HasRAI(): return uliFields.RAI.MCCFromPLMN(), nil case uliFields.HasTAI(): return uliFields.TAI.MCCFromPLMN(), nil case uliFields.HasECGI(): return uliFields.ECGI.MCCFromPLMN(), nil case uliFields.HasLAI(): return uliFields.LAI.MCCFromPLMN(), nil case uliFields.HasMENBI(): return uliFields.MENBI.MCCFromPLMN(), nil case uliFields.HasEMENBI(): return uliFields.EMENBI.MCCFromPLMN(), nil } return "", errors.New("MCC is not present in ULI") default: return "", &InvalidTypeError{Type: i.Type} } } // MustMCC returns MCC in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMCC() string { v, _ := i.MCC() return v } // MNC returns MNC in string if the type of IE matches. func (i *IE) MNC() (string, error) { if len(i.Payload) < 3 { return "", io.ErrUnexpectedEOF } switch i.Type { case ServingNetwork, PLMNID: _, mnc, err := utils.DecodePLMN(i.Payload) if err != nil { return "", err } return mnc, nil case GlobalCNID, TraceReference, GUTI, UserCSGInformation: _, mnc, err := utils.DecodePLMN(i.Payload[:3]) if err != nil { return "", err } return mnc, nil case UserLocationInformation: uliFields, err := i.UserLocationInformation() if err != nil { return "", err } switch { case uliFields.HasCGI(): return uliFields.CGI.MNCFromPLMN(), nil case uliFields.HasSAI(): return uliFields.SAI.MNCFromPLMN(), nil case uliFields.HasRAI(): return uliFields.RAI.MNCFromPLMN(), nil case uliFields.HasTAI(): return uliFields.TAI.MNCFromPLMN(), nil case uliFields.HasECGI(): return uliFields.ECGI.MNCFromPLMN(), nil case uliFields.HasLAI(): return uliFields.LAI.MNCFromPLMN(), nil case uliFields.HasMENBI(): return uliFields.MENBI.MNCFromPLMN(), nil case uliFields.HasEMENBI(): return uliFields.EMENBI.MNCFromPLMN(), nil } return "", errors.New("MNC is not present in ULI") default: return "", &InvalidTypeError{Type: i.Type} } } // MustMNC returns MNC in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMNC() string { v, _ := i.MNC() return v } ================================================ FILE: gtpv2/ie/mcc-mnc_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "testing" ) func TestMCCAndMNCFromULI(t *testing.T) { tests := []struct { name string i *IE wantMCC string wantMNC string wantErr bool }{ { name: "Test getting MCC and MNC from ULI from CGI", i: NewUserLocationInformationStruct( NewCGI("123", "45", 0x1111, 0x2222), nil, nil, nil, nil, nil, nil, nil, ), wantMCC: "123", wantMNC: "45", }, { name: "Test getting MCC and MNC from ULI from SAI", i: NewUserLocationInformationStruct( nil, NewSAI("234", "56", 0x1111, 0x3333), nil, nil, nil, nil, nil, nil, ), wantMCC: "234", wantMNC: "56", }, { name: "Test getting MCC and MNC from ULI from RAI", i: NewUserLocationInformationStruct( nil, nil, NewRAI("456", "78", 0x1111, 0x4444), nil, nil, nil, nil, nil, ), wantMCC: "456", wantMNC: "78", }, { name: "Test getting MCC and MNC from ULI from TAI", i: NewUserLocationInformationStruct( nil, nil, nil, NewTAI("567", "89", 0x5555), nil, nil, nil, nil, ), wantMCC: "567", wantMNC: "89", }, { name: "Test getting MCC and MNC from ULI from ECGI", i: NewUserLocationInformationStruct( nil, nil, nil, nil, NewECGI("678", "90", 0x66666666), nil, nil, nil, ), wantMCC: "678", wantMNC: "90", }, { name: "Test getting MCC and MNC from ULI from LAI", i: NewUserLocationInformationStruct( nil, nil, nil, nil, nil, NewLAI("789", "01", 0x1111), nil, nil, ), wantMCC: "789", wantMNC: "01", }, { name: "Test getting MCC and MNC from ULI from MENBI", i: NewUserLocationInformationStruct( nil, nil, nil, nil, nil, nil, NewMENBI("890", "12", 0x11111111), nil, ), wantMCC: "890", wantMNC: "12", }, { name: "Test getting MCC and MNC from ULI from EMENBI", i: NewUserLocationInformationStruct( nil, nil, nil, nil, nil, nil, nil, NewEMENBI("321", "54", 0x22222222), ), wantMCC: "321", wantMNC: "54", }, { name: "Test getting empty strings when uli does not contain information elemtents", i: NewUserLocationInformationStruct( nil, nil, nil, nil, nil, nil, nil, nil, ), wantMCC: "", wantMNC: "", wantErr: true, }, { name: "Test getting MCC and MNC from ULI from CGI when all elements are given", i: NewUserLocationInformationStruct( NewCGI("123", "45", 0x1111, 0x2222), NewSAI("234", "56", 0x1111, 0x3333), NewRAI("456", "78", 0x1111, 0x4444), NewTAI("567", "89", 0x5555), NewECGI("678", "90", 0x66666666), NewLAI("789", "01", 0x1111), NewMENBI("890", "12", 0x11111111), NewEMENBI("321", "54", 0x22222222), ), wantMCC: "123", wantMNC: "45", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := tt.i.MCC() if (err != nil) != tt.wantErr { t.Errorf("IE.MCC() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.wantMCC { t.Errorf("IE.MCC() = %v, want %v", got, tt.wantMCC) } got, err = tt.i.MNC() if (err != nil) != tt.wantErr { t.Errorf("IE.MNC() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.wantMNC { t.Errorf("IE.MNC() = %v, want %v", got, tt.wantMNC) } }) } } ================================================ FILE: gtpv2/ie/mei.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "strings" "github.com/wmnsk/go-gtp/utils" ) // NewMobileEquipmentIdentity creates a new MobileEquipmentIdentity IE. func NewMobileEquipmentIdentity(mei string) *IE { m, err := utils.StrToSwappedBytes(mei, "f") if err != nil { return nil } return New(MobileEquipmentIdentity, 0x00, m) } // MobileEquipmentIdentity returns MobileEquipmentIdentity in string if the // type of IE matches. func (i *IE) MobileEquipmentIdentity() (string, error) { if i.Type != MobileEquipmentIdentity { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return "", io.ErrUnexpectedEOF } str := utils.SwappedBytesToStr(i.Payload, false) return strings.TrimSuffix(str, "f"), nil } // MustMobileEquipmentIdentity returns MobileEquipmentIdentity in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMobileEquipmentIdentity() string { v, _ := i.MobileEquipmentIdentity() return v } ================================================ FILE: gtpv2/ie/msisdn.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "strings" "github.com/wmnsk/go-gtp/utils" ) // NewMSISDN creates a new MSISDN IE. func NewMSISDN(msisdn string) *IE { m, err := utils.StrToSwappedBytes(msisdn, "f") if err != nil { return nil } return New(MSISDN, 0x00, m) } // MSISDN returns MSISDN in string if the type of IE matches. func (i *IE) MSISDN() (string, error) { if i.Type != MSISDN { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return "", io.ErrUnexpectedEOF } str := utils.SwappedBytesToStr(i.Payload, false) return strings.TrimSuffix(str, "f"), nil } // MustMSISDN returns MSISDN in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustMSISDN() string { v, _ := i.MSISDN() return v } ================================================ FILE: gtpv2/ie/node-features.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewNodeFeatures creates a new NodeFeatures IE. func NewNodeFeatures(flags uint8) *IE { return NewUint8IE(NodeFeatures, flags) } // NodeFeatures returns NodeFeatures in uint8 if the type of IE matches. func (i *IE) NodeFeatures() (uint8, error) { if i.Type != NodeFeatures { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustNodeFeatures returns NodeFeatures in uint8 if the type of IE matches. func (i *IE) MustNodeFeatures() uint8 { v, _ := i.NodeFeatures() return v } // HasPRN reports whether an IE has PRN bit. func (i *IE) HasPRN() bool { v, err := i.NodeFeatures() if err != nil { return false } return has1stBit(v) } // HasMABR reports whether an IE has MABR bit. func (i *IE) HasMABR() bool { v, err := i.NodeFeatures() if err != nil { return false } return has2ndBit(v) } // HasNTSR reports whether an IE has NTSR bit. func (i *IE) HasNTSR() bool { v, err := i.NodeFeatures() if err != nil { return false } return has3rdBit(v) } // HasCIOT reports whether an IE has CIOT bit. func (i *IE) HasCIOT() bool { v, err := i.NodeFeatures() if err != nil { return false } return has4thBit(v) } // HasS1UN reports whether an IE has S1UN bit. func (i *IE) HasS1UN() bool { v, err := i.NodeFeatures() if err != nil { return false } return has5thBit(v) } // HasETH reports whether an IE has ETH bit. func (i *IE) HasETH() bool { v, err := i.NodeFeatures() if err != nil { return false } return has6thBit(v) } // HasMTEDT reports whether an IE has MTEDT bit. func (i *IE) HasMTEDT() bool { v, err := i.NodeFeatures() if err != nil { return false } return has7thBit(v) } ================================================ FILE: gtpv2/ie/node-type.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewNodeType creates a new NodeType IE. func NewNodeType(nodeType uint8) *IE { return NewUint8IE(NodeType, nodeType) } // NodeType returns NodeType in uint8 if the type of IE matches. func (i *IE) NodeType() (uint8, error) { if i.Type != NodeType { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustNodeType returns NodeType in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustNodeType() uint8 { v, _ := i.NodeType() return v } ================================================ FILE: gtpv2/ie/p-tmsi-signature.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewPTMSISignature creates a new PTMSISignature IE. func NewPTMSISignature(sig uint32) *IE { return New(PTMSISignature, 0x00, utils.Uint32To24(sig)) } // PTMSISignature returns PTMSISignature value in uint32 if type matches. func (i *IE) PTMSISignature() (uint32, error) { if i.Type != PTMSISignature { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return 0, io.ErrUnexpectedEOF } return utils.Uint24To32(i.Payload), nil } // MustPTMSISignature returns PTMSISignature in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustPTMSISignature() uint32 { v, _ := i.PTMSISignature() return v } ================================================ FILE: gtpv2/ie/p-tmsi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewPacketTMSI creates a new PacketTMSI IE. func NewPacketTMSI(ptmsi uint32) *IE { return NewUint32IE(PacketTMSI, ptmsi) } // PacketTMSI returns PacketTMSI value in uint32 if type matches. func (i *IE) PacketTMSI() (uint32, error) { if i.Type != PacketTMSI { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint32() } // MustPacketTMSI returns PacketTMSI in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustPacketTMSI() uint32 { v, _ := i.PacketTMSI() return v } ================================================ FILE: gtpv2/ie/paa.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "net" ) // PDN Type definitions. const ( _ uint8 = iota pdnTypeIPv4 pdnTypeIPv6 pdnTypeIPv4v6 pdnTypeNonIP ) // NewPDNAddressAllocation creates a new PDNAddressAllocation IE. // // The PDN Type field is automatically judged by the format of given addr, // If it cannot be converted as neither IPv4 nor IPv6, PDN Type will be Non-IP. // // NOTE: IPv6 Prefix Length will be set to 0 when an IPv6 address is given to this. // Use NewPDNAddressAllocationIPv6 instead to set the correct value. func NewPDNAddressAllocation(addr string) *IE { return NewPDNAddressAllocationNetIP(net.ParseIP(addr), 0) } // NewPDNAddressAllocation creates a new PDNAddressAllocation IE with IPv6 value. func NewPDNAddressAllocationIPv6(addr string, prefix uint8) *IE { return NewPDNAddressAllocationNetIP(net.ParseIP(addr), prefix) } // NewPDNAddressAllocationDual creates a new PDNAddressAllocation IE with // IPv4 address and IPv6 address given. // // If they cannot be converted as IPv4/IPv6, PDN Type will be Non-IP. func NewPDNAddressAllocationDual(v4addr, v6addr string, v6prefix uint8) *IE { return NewPDNAddressAllocationDualNetIP(net.ParseIP(v4addr), net.ParseIP(v6addr), v6prefix) } // NewPDNAddressAllocationNetIP creates a new PDNAddressAllocation IE from net.IP. func NewPDNAddressAllocationNetIP(ip net.IP, v6prefix uint8) *IE { var v *PDNAddressAllocationFields if v4 := ip.To4(); v4 != nil { v = NewPDNAddressAllocationFields(pdnTypeIPv4, v4, nil, 0) } else if v6 := ip.To16(); v6 != nil { v = NewPDNAddressAllocationFields(pdnTypeIPv6, nil, v6, v6prefix) } else { v = NewPDNAddressAllocationFields(pdnTypeNonIP, nil, nil, 0) } b, err := v.Marshal() if err != nil { return nil } return New(PDNAddressAllocation, 0x00, b) } // NewPDNAddressAllocationDualNetIP creates a new PDNAddressAllocation IE from // IPv4 and IPv6 in net.IP. func NewPDNAddressAllocationDualNetIP(v4, v6 net.IP, v6prefix uint8) *IE { v := NewPDNAddressAllocationFields(pdnTypeIPv4v6, v4, v6, v6prefix) b, err := v.Marshal() if err != nil { return nil } return New(PDNAddressAllocation, 0x00, b) } // PDNAddressAllocationFields is a set of fields in PDNAddressAllocation IE. type PDNAddressAllocationFields struct { PDNType uint8 // 3-bit IPv4Address net.IP IPv6PrefixLength uint8 IPv6Address net.IP } // NewPDNAddressAllocationFields creates a new PDNAddressAllocationFields. func NewPDNAddressAllocationFields(pType uint8, v4, v6 net.IP, prefix uint8) *PDNAddressAllocationFields { return &PDNAddressAllocationFields{ PDNType: pType, IPv4Address: v4, IPv6PrefixLength: prefix, IPv6Address: v6, } } // Marshal serializes PDNAddressAllocationFields. func (f *PDNAddressAllocationFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes PDNAddressAllocationFields. func (f *PDNAddressAllocationFields) MarshalTo(b []byte) error { l := len(b) if l < 1 { return io.ErrUnexpectedEOF } b[0] = f.PDNType & 0x07 offset := 1 switch f.PDNType { case pdnTypeIPv4: if l < offset+4 { return io.ErrUnexpectedEOF } if f.IPv4Address != nil { copy(b[offset:offset+4], f.IPv4Address.To4()) } return nil case pdnTypeIPv6: if l < offset+17 { return io.ErrUnexpectedEOF } b[offset] = f.IPv6PrefixLength copy(b[offset+1:offset+17], f.IPv6Address.To16()) return nil case pdnTypeIPv4v6: if l < offset+21 { return io.ErrUnexpectedEOF } b[offset] = f.IPv6PrefixLength copy(b[offset+1:offset+17], f.IPv6Address.To16()) copy(b[offset+17:offset+21], f.IPv4Address.To4()) return nil case pdnTypeNonIP: // no payload return nil default: return nil } } // ParsePDNAddressAllocationFields decodes PDNAddressAllocationFields. func ParsePDNAddressAllocationFields(b []byte) (*PDNAddressAllocationFields, error) { f := &PDNAddressAllocationFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into PDNAddressAllocationFields. func (f *PDNAddressAllocationFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 1 { return io.ErrUnexpectedEOF } f.PDNType = b[0] & 0x07 offset := 1 switch f.PDNType { case pdnTypeIPv4: if l < offset+4 { return io.ErrUnexpectedEOF } f.IPv4Address = b[offset : offset+4] return nil case pdnTypeIPv6: if l < offset+17 { return io.ErrUnexpectedEOF } f.IPv6PrefixLength = b[offset] f.IPv6Address = b[offset+1 : offset+17] return nil case pdnTypeIPv4v6: if l < offset+21 { return io.ErrUnexpectedEOF } f.IPv6PrefixLength = b[offset] f.IPv6Address = b[offset+1 : offset+17] f.IPv4Address = b[offset+17 : offset+21] return nil case pdnTypeNonIP: // no payload return nil default: return nil } } // MarshalLen returns the serial length of PDNAddressAllocationFields in int. func (f *PDNAddressAllocationFields) MarshalLen() int { l := 1 switch f.PDNType { case pdnTypeIPv4: l += 4 case pdnTypeIPv6: l += 17 case pdnTypeIPv4v6: l += 21 case pdnTypeNonIP: // no payload, do nothing } return l } ================================================ FILE: gtpv2/ie/paging-and-service-information.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" ) // NewPagingAndServiceInformation creates a new PagingAndServiceInformation IE. func NewPagingAndServiceInformation(ebi, flags, ppi uint8) *IE { v := NewPagingAndServiceInformationFields(ebi, flags, ppi) b, err := v.Marshal() if err != nil { return nil } return New(PagingAndServiceInformation, 0x00, b) } // PagingAndServiceInformation returns PagingAndServiceInformation in PagingAndServiceInformationFields type if the type of IE matches. func (i *IE) PagingAndServiceInformation() (*PagingAndServiceInformationFields, error) { switch i.Type { case PagingAndServiceInformation: return ParsePagingAndServiceInformationFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // PagingAndServiceInformationFields is a set of fields in PagingAndServiceInformation IE. type PagingAndServiceInformationFields struct { EPSBearerID uint8 // 4-bit Flags uint8 // 1st bit PagingPolicyIndication uint8 // 7-bit } // NewPagingAndServiceInformationFields creates a new PagingAndServiceInformationFields. func NewPagingAndServiceInformationFields(ebi, flags, ppi uint8) *PagingAndServiceInformationFields { return &PagingAndServiceInformationFields{ EPSBearerID: ebi & 0x0f, Flags: flags, // no mask, may be used in the future PagingPolicyIndication: ppi & 0x7f, } } // Marshal serializes PagingAndServiceInformationFields. func (f *PagingAndServiceInformationFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes PagingAndServiceInformationFields. func (f *PagingAndServiceInformationFields) MarshalTo(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } b[0] = f.EPSBearerID & 0x0f b[1] = f.Flags if has1stBit(f.Flags) { if l < 3 { return io.ErrUnexpectedEOF } b[2] = f.PagingPolicyIndication & 0x7f } return nil } // ParsePagingAndServiceInformationFields decodes PagingAndServiceInformationFields. func ParsePagingAndServiceInformationFields(b []byte) (*PagingAndServiceInformationFields, error) { f := &PagingAndServiceInformationFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into PagingAndServiceInformationFields. func (f *PagingAndServiceInformationFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } f.EPSBearerID = b[0] & 0x0f f.Flags = b[0] if has1stBit(f.Flags) { if l < 3 { return io.ErrUnexpectedEOF } f.PagingPolicyIndication = b[2] & 0x7f } return nil } // MarshalLen returns the serial length of PagingAndServiceInformationFields in int. func (f *PagingAndServiceInformationFields) MarshalLen() int { l := 2 if has1stBit(f.Flags) { l++ } return l } // PagingPolicyIndication returns PagingPolicyIndication in uint8 if the type of IE matches. func (i *IE) PagingPolicyIndication() (uint8, error) { switch i.Type { case PagingAndServiceInformation: f, err := ParsePagingAndServiceInformationFields(i.Payload) if err != nil { return 0, err } if !has1stBit(f.Flags) { return 0, ErrIEValueNotFound } return f.PagingPolicyIndication, nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustPagingPolicyIndication returns PagingPolicyIndication in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustPagingPolicyIndication() uint8 { v, _ := i.PagingPolicyIndication() return v } ================================================ FILE: gtpv2/ie/pco-ppp.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "net" ) const ( PCOPPPConfigurationRequest = 0x01 PCOPPPConfigurationAck = 0x02 ) // PCOPPP represents a PPP header and its contents used in PCO. // // TODO: create another package with full implementation. type PCOPPP struct { Code uint8 Identifier uint8 Length uint16 Payload []byte } // NewPCOPPP creates a new PCOPPP. func NewPCOPPP(code, id uint8, payload []byte) *PCOPPP { return &PCOPPP{ Code: code, Identifier: id, Length: uint16(4 + len(payload)), Payload: payload, } } // NewPCOPPPWithPAP creates a new PCOPPP with given PAP. func NewPCOPPPWithPAP(code, id uint8, peer, pass string) *PCOPPP { pap, err := NewPAPFields(peer, pass).Marshal() if err != nil { return nil } return NewPCOPPP(code, id, pap) } // NewPCOPPPWithCHAP creates a new PCOPPP with given CHAP. func NewPCOPPPWithCHAP(code, id uint8, val []byte, pass string) *PCOPPP { pap, err := NewCHAPFields(val, pass).Marshal() if err != nil { return nil } return NewPCOPPP(code, id, pap) } // NewPCOPPPWithIPCPOptions creates a new PCOPPP with given IPCPOptions. func NewPCOPPPWithIPCPOptions(code, id uint8, opts ...*IPCPOption) *PCOPPP { offset := 0 b := make([]byte, offset) for _, o := range opts { l := o.MarshalLen() b = append(b, make([]byte, l)...) if err := o.MarshalTo(b[offset : offset+l]); err != nil { return nil } offset += l } return NewPCOPPP(code, id, b) } // Marshal serializes PCOPPP. func (p *PCOPPP) Marshal() ([]byte, error) { b := make([]byte, p.MarshalLen()) if err := p.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes PCOPPP. func (p *PCOPPP) MarshalTo(b []byte) error { if len(b) < 5 { return io.ErrUnexpectedEOF } b[0] = p.Code b[1] = p.Identifier binary.BigEndian.PutUint16(b[2:4], p.Length) copy(b[4:], p.Payload) return nil } // ParsePCOPPP decodes PCOPPP. func ParsePCOPPP(b []byte) (*PCOPPP, error) { p := &PCOPPP{} if err := p.UnmarshalBinary(b); err != nil { return nil, err } return p, nil } // UnmarshalBinary decodes given bytes into PCOPPP. func (p *PCOPPP) UnmarshalBinary(b []byte) error { l := len(b) if l < 5 { return ErrTooShortToParse } p.Code = b[0] p.Identifier = b[1] p.Length = binary.BigEndian.Uint16(b[2:4]) if l < int(p.Length) { return io.ErrUnexpectedEOF } p.Payload = b[4:int(p.Length)] return nil } // MarshalLen returns the serial length of PCOPPP in int. func (p *PCOPPP) MarshalLen() int { return 4 + len(p.Payload) } // PAPFields represents a PAP payload on PPP protocol. // // TODO: create another package with full implementation. type PAPFields struct { PeerIDLength uint8 PeerID string PasswordLength uint8 Password string } // NewPAPFields creates a new PAPFields. func NewPAPFields(id, pass string) *PAPFields { return &PAPFields{ PeerIDLength: uint8(len([]byte(id))), PeerID: id, PasswordLength: uint8(len([]byte(pass))), Password: pass, } } // Marshal serializes PAPFields. func (p *PAPFields) Marshal() ([]byte, error) { b := make([]byte, p.MarshalLen()) if err := p.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes PAPFields. func (p *PAPFields) MarshalTo(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } b[0] = p.PeerIDLength offset := 1 if l < offset+int(p.PeerIDLength) { return io.ErrUnexpectedEOF } copy(b[offset:offset+int(p.PeerIDLength)], p.PeerID) offset += int(p.PeerIDLength) b[offset] = p.PasswordLength offset++ if l < offset+int(p.PasswordLength) { return io.ErrUnexpectedEOF } copy(b[offset:offset+int(p.PasswordLength)], p.Password) return nil } // ParsePAPFields decodes PAPFields. func ParsePAPFields(b []byte) (*PAPFields, error) { p := &PAPFields{} if err := p.UnmarshalBinary(b); err != nil { return nil, err } return p, nil } // UnmarshalBinary decodes given bytes into PAPFields. func (p *PAPFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 2 { return ErrTooShortToParse } p.PeerIDLength = b[0] offset := 1 if l < offset+int(p.PeerIDLength) { return io.ErrUnexpectedEOF } p.PeerID = string(b[offset : offset+int(p.PeerIDLength)]) offset += int(p.PeerIDLength) p.PasswordLength = b[offset] offset++ if l < offset+int(p.PasswordLength) { return io.ErrUnexpectedEOF } p.Password = string(b[offset : offset+int(p.PasswordLength)]) return nil } // MarshalLen returns the serial length of PAPFields in int. func (p *PAPFields) MarshalLen() int { return 2 + len([]byte(p.PeerID)) + len([]byte(p.Password)) } // CHAPFields represents a PAP payload on PPP protocol. // // TODO: create another package with full implementation. type CHAPFields struct { ValueSize uint8 Value []byte Name string } // NewCHAPFields creates a new CHAPFields. func NewCHAPFields(val []byte, name string) *CHAPFields { return &CHAPFields{ ValueSize: uint8(len(val)), Value: val, Name: name, } } // Marshal serializes CHAPFields. func (c *CHAPFields) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes CHAPFields. func (c *CHAPFields) MarshalTo(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } b[0] = c.ValueSize offset := 1 if l < offset+int(c.ValueSize) { return io.ErrUnexpectedEOF } copy(b[offset:offset+int(c.ValueSize)], c.Value) offset += int(c.ValueSize) copy(b[offset:], c.Name) return nil } // ParseCHAPFields decodes CHAPFields. func ParseCHAPFields(b []byte) (*CHAPFields, error) { c := &CHAPFields{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes into CHAPFields. func (c *CHAPFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 2 { return ErrTooShortToParse } c.ValueSize = b[0] offset := 1 if l < offset+int(c.ValueSize) { return io.ErrUnexpectedEOF } c.Value = b[offset : offset+int(c.ValueSize)] offset += int(c.ValueSize) c.Name = string(b[offset:]) return nil } // MarshalLen returns the serial length of CHAPFields in int. func (c *CHAPFields) MarshalLen() int { return 1 + len(c.Value) + len([]byte(c.Name)) } // IPCP Options. // // TODO: perhaps there are more options but... const ( IPCPOptionIPAddress uint8 = 3 IPCPOptionMobileIPv4 uint8 = 4 IPCPOptionPrimaryDNS uint8 = 129 IPCPOptionSecondaryDNS uint8 = 131 ) // IPCPOption is a IPCP Option. type IPCPOption struct { Type uint8 Length uint8 Payload []byte } // NewIPCPOption creates an IPCPOption with given IP address. func NewIPCPOption(typ uint8, payload []byte) *IPCPOption { return &IPCPOption{ Type: typ, Length: uint8(2 + len(payload)), Payload: payload, } } // NewIPCPOptionIPAddress creates an IPCPOption with given IP address. func NewIPCPOptionIPAddress(ip net.IP) *IPCPOption { v4 := ip.To4() // IPv4 if v4 != nil { return NewIPCPOption(IPCPOptionIPAddress, v4) } // IPv6 return NewIPCPOption(IPCPOptionIPAddress, ip) } // NewIPCPOptionMobileIPv4 creates an IPCPOption with given IP address. func NewIPCPOptionMobileIPv4(ip net.IP) *IPCPOption { v4 := ip.To4() // IPv4 if v4 != nil { return NewIPCPOption(IPCPOptionMobileIPv4, v4) } // IPv6 return NewIPCPOption(IPCPOptionMobileIPv4, ip) } // NewIPCPOptionPrimaryDNS creates an IPCPOption with given IP address. func NewIPCPOptionPrimaryDNS(ip net.IP) *IPCPOption { v4 := ip.To4() // IPv4 if v4 != nil { return NewIPCPOption(IPCPOptionPrimaryDNS, v4) } // IPv6 return NewIPCPOption(IPCPOptionPrimaryDNS, ip) } // NewIPCPOptionSecondaryDNS creates an IPCPOption with given IP address. func NewIPCPOptionSecondaryDNS(ip net.IP) *IPCPOption { v4 := ip.To4() // IPv4 if v4 != nil { return NewIPCPOption(IPCPOptionSecondaryDNS, v4) } // IPv6 return NewIPCPOption(IPCPOptionSecondaryDNS, ip) } // Marshal serializes IPCPOption. func (o *IPCPOption) Marshal() ([]byte, error) { b := make([]byte, o.MarshalLen()) if err := o.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes IPCPOption. func (o *IPCPOption) MarshalTo(b []byte) error { if len(b) < 3 { return io.ErrUnexpectedEOF } b[0] = o.Type b[1] = o.Length copy(b[2:], o.Payload) return nil } // ParseIPCPOption decodes IPCPOption. func ParseIPCPOption(b []byte) (*IPCPOption, error) { o := &IPCPOption{} if err := o.UnmarshalBinary(b); err != nil { return nil, err } return o, nil } // UnmarshalBinary decodes given bytes into IPCPOption. func (o *IPCPOption) UnmarshalBinary(b []byte) error { l := len(b) if l < 3 { return ErrTooShortToParse } o.Type = b[0] o.Length = b[1] if l < int(o.Length) { return io.ErrUnexpectedEOF } o.Payload = b[2:int(o.Length)] return nil } // MarshalLen returns the serial length of IPCPOption in int. func (o *IPCPOption) MarshalLen() int { return 2 + len(o.Payload) } ================================================ FILE: gtpv2/ie/pco-ppp_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie_test import ( "net" "testing" "github.com/google/go-cmp/cmp" "github.com/wmnsk/go-gtp/gtpv2/ie" ) var ( ip1 = net.ParseIP("1.1.1.1") ip2 = net.ParseIP("2.2.2.2") ) func TestPCOPPP(t *testing.T) { cases := []struct { description string structured *ie.PCOPPP serialized []byte }{ { "PAP", ie.NewPCOPPPWithPAP(ie.PCOPPPConfigurationRequest, 0, "foo", "bar"), []byte{0x01, 0x00, 0x00, 0x0c, 0x03, 0x66, 0x6f, 0x6f, 0x03, 0x62, 0x61, 0x72}, }, { "CHAP", ie.NewPCOPPPWithCHAP(ie.PCOPPPConfigurationRequest, 0, []byte{0xde, 0xad, 0xbe, 0xef}, "foo"), []byte{0x01, 0x00, 0x00, 0x0c, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x66, 0x6f, 0x6f}, }, { "IPCPOptions/IPAddress+PrimaryDNS", ie.NewPCOPPPWithIPCPOptions( ie.PCOPPPConfigurationRequest, 0, ie.NewIPCPOptionIPAddress(ip1), ie.NewIPCPOptionPrimaryDNS(ip2), ), []byte{0x01, 0x00, 0x00, 0x10, 0x03, 0x06, 0x01, 0x01, 0x01, 0x01, 0x81, 0x06, 0x02, 0x02, 0x02, 0x02}, }, } for _, c := range cases { t.Run("serialize/"+c.description, func(t *testing.T) { got, err := c.structured.Marshal() if err != nil { t.Fatal(err) } if diff := cmp.Diff(got, c.serialized); diff != "" { t.Error(diff) } }) t.Run("decode/"+c.description, func(t *testing.T) { got, err := ie.ParsePCOPPP(c.serialized) if err != nil { t.Fatal(err) } opt := cmp.AllowUnexported(*got, *c.structured) if diff := cmp.Diff(got, c.structured, opt); diff != "" { t.Error(diff) } }) } } func TestPCOPPPPAPFields(t *testing.T) { cases := []struct { description string structured *ie.PAPFields serialized []byte }{ { "Normal", ie.NewPAPFields("foo", "bar"), []byte{0x03, 0x66, 0x6f, 0x6f, 0x03, 0x62, 0x61, 0x72}, }, } for _, c := range cases { t.Run("serialize/"+c.description, func(t *testing.T) { got, err := c.structured.Marshal() if err != nil { t.Fatal(err) } if diff := cmp.Diff(got, c.serialized); diff != "" { t.Error(diff) } }) t.Run("decode/"+c.description, func(t *testing.T) { got, err := ie.ParsePAPFields(c.serialized) if err != nil { t.Fatal(err) } opt := cmp.AllowUnexported(*got, *c.structured) if diff := cmp.Diff(got, c.structured, opt); diff != "" { t.Error(diff) } }) } } func TestPCOPPPCHAPFields(t *testing.T) { cases := []struct { description string structured *ie.CHAPFields serialized []byte }{ { "Normal", ie.NewCHAPFields([]byte{0xde, 0xad, 0xbe, 0xef}, "foo"), []byte{0x04, 0xde, 0xad, 0xbe, 0xef, 0x66, 0x6f, 0x6f}, }, } for _, c := range cases { t.Run("serialize/"+c.description, func(t *testing.T) { got, err := c.structured.Marshal() if err != nil { t.Fatal(err) } if diff := cmp.Diff(got, c.serialized); diff != "" { t.Error(diff) } }) t.Run("decode/"+c.description, func(t *testing.T) { got, err := ie.ParseCHAPFields(c.serialized) if err != nil { t.Fatal(err) } opt := cmp.AllowUnexported(*got, *c.structured) if diff := cmp.Diff(got, c.structured, opt); diff != "" { t.Error(diff) } }) } } func TestPCOPPPIPCPOption(t *testing.T) { cases := []struct { description string structured *ie.IPCPOption serialized []byte }{ { "IPAddress", ie.NewIPCPOptionIPAddress(ip1), []byte{0x03, 0x06, 0x01, 0x01, 0x01, 0x01}, }, { "PrimaryDNS", ie.NewIPCPOptionPrimaryDNS(ip2), []byte{0x81, 0x06, 0x02, 0x02, 0x02, 0x02}, }, } for _, c := range cases { t.Run("serialize/"+c.description, func(t *testing.T) { got, err := c.structured.Marshal() if err != nil { t.Fatal(err) } if diff := cmp.Diff(got, c.serialized); diff != "" { t.Error(diff) } }) t.Run("decode/"+c.description, func(t *testing.T) { got, err := ie.ParseIPCPOption(c.serialized) if err != nil { t.Fatal(err) } opt := cmp.AllowUnexported(*got, *c.structured) if diff := cmp.Diff(got, c.structured, opt); diff != "" { t.Error(diff) } }) } } ================================================ FILE: gtpv2/ie/pco.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "fmt" "io" ) // ConfigurationProtocol definitions. const ( ConfigurationProtocolPPPForUseWithIPPDPTypeOrIPPDNType uint8 = 0b000 ) // NewProtocolConfigurationOptions creates a new ProtocolConfigurationOptions IE. func NewProtocolConfigurationOptions(proto uint8, options ...*PCOContainer) *IE { v := NewProtocolConfigurationOptionsFields(proto, options...) b, err := v.Marshal() if err != nil { return nil } return New(ProtocolConfigurationOptions, 0x00, b) } // ProtocolConfigurationOptions returns ProtocolConfigurationOptions in // ProtocolConfigurationOptionsFields type if the type of IE matches. func (i *IE) ProtocolConfigurationOptions() (*ProtocolConfigurationOptionsFields, error) { switch i.Type { case ProtocolConfigurationOptions: if len(i.Payload) < 1 { return nil, io.ErrUnexpectedEOF } return ParseProtocolConfigurationOptionsFields(i.Payload) case BearerContext: ies, err := i.BearerContext() if err != nil { return nil, fmt.Errorf("failed to retrieve ProtocolConfigurationOptions: %w", err) } for _, child := range ies { if child.Type == ProtocolConfigurationOptions { return child.ProtocolConfigurationOptions() } } return nil, ErrIENotFound default: return nil, &InvalidTypeError{Type: i.Type} } } // MustProtocolConfigurationOptions returns ProtocolConfigurationOptions in *ProtocolConfigurationOptionsFields, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustProtocolConfigurationOptions() *ProtocolConfigurationOptionsFields { v, _ := i.ProtocolConfigurationOptions() return v } // ProtocolConfigurationOptionsFields is a set of fields in ProtocolConfigurationOptions IE. type ProtocolConfigurationOptionsFields struct { Extension uint8 // bit 8 of octet 1 ConfigurationProtocol uint8 // bit 1-3 of octet 1 ProtocolOrContainers []*PCOContainer } // NewProtocolConfigurationOptionsFields creates a new ProtocolConfigurationOptionsFields. func NewProtocolConfigurationOptionsFields(proto uint8, opts ...*PCOContainer) *ProtocolConfigurationOptionsFields { f := &ProtocolConfigurationOptionsFields{ConfigurationProtocol: proto} f.ProtocolOrContainers = append(f.ProtocolOrContainers, opts...) return f } // Marshal serializes ProtocolConfigurationOptionsFields. func (f *ProtocolConfigurationOptionsFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ProtocolConfigurationOptionsFields. func (f *ProtocolConfigurationOptionsFields) MarshalTo(b []byte) error { b[0] = (f.ConfigurationProtocol & 0x07) | 0x80 offset := 1 for _, opt := range f.ProtocolOrContainers { if err := opt.MarshalTo(b[offset:]); err != nil { return err } offset += opt.MarshalLen() } return nil } // ParseProtocolConfigurationOptionsFields decodes ProtocolConfigurationOptionsFields. func ParseProtocolConfigurationOptionsFields(b []byte) (*ProtocolConfigurationOptionsFields, error) { f := &ProtocolConfigurationOptionsFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into ProtocolConfigurationOptionsFields. func (f *ProtocolConfigurationOptionsFields) UnmarshalBinary(b []byte) error { if len(b) < 1 { return ErrTooShortToParse } f.Extension = (b[0] >> 7) & 0x01 f.ConfigurationProtocol = b[0] & 0x07 offset := 1 for { if offset >= len(b) { return nil } opt, err := ParsePCOContainer(b[offset:]) if err != nil { return err } f.ProtocolOrContainers = append(f.ProtocolOrContainers, opt) offset += opt.MarshalLen() } } // MarshalLen returns the serial length of ProtocolConfigurationOptionsFields in int. func (f *ProtocolConfigurationOptionsFields) MarshalLen() int { l := 1 for _, opt := range f.ProtocolOrContainers { l += opt.MarshalLen() } return l } // ProtocolIdentifier definitions. // // [Table 10.5.154/3GPP TS 24.008] // // At least the following protocol identifiers (as defined in RFC 3232 [103]) shall be // supported in this version of the protocol: // - C021H (LCP); // - C023H (PAP); // - C223H (CHAP); and // - 8021H (IPCP). const ( PCOProtocolIdentifierLCP uint16 = 0xc021 PCOProtocolIdentifierPAP uint16 = 0xc023 PCOProtocolIdentifierCHAP uint16 = 0xc223 PCOProtocolIdentifierIPCP uint16 = 0x8021 ) // PCOContainer is either of a Configuration protocol option or Additional parameters in PCO, // which are not distinguishable without meta information(link direction) but fortunately // the format is the same. type PCOContainer struct { ID uint16 Length uint8 Contents []byte } // NewPCOContainer creates a new PCOContainer. func NewPCOContainer(pid uint16, contents []byte) *PCOContainer { c := &PCOContainer{ ID: pid, Length: uint8(len(contents)), Contents: contents, } return c } // Marshal serializes PCOContainer. func (c *PCOContainer) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes PCOContainer. func (c *PCOContainer) MarshalTo(b []byte) error { binary.BigEndian.PutUint16(b[0:2], c.ID) b[2] = c.Length if c.Length != 0 { copy(b[3:], c.Contents) } return nil } // ParsePCOContainer decodes PCOContainer. func ParsePCOContainer(b []byte) (*PCOContainer, error) { c := &PCOContainer{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes into PCOContainer. func (c *PCOContainer) UnmarshalBinary(b []byte) error { l := len(b) if l < 3 { return ErrTooShortToParse } c.ID = binary.BigEndian.Uint16(b[0:2]) c.Length = b[2] if c.Length != 0 && l >= 3+int(c.Length) { c.Contents = make([]byte, c.Length) copy(c.Contents, b[3:3+int(c.Length)]) } return nil } // MarshalLen returns the serial length of PCOContainer in int. func (c *PCOContainer) MarshalLen() int { return 3 + len(c.Contents) } ================================================ FILE: gtpv2/ie/pdn-type.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewPDNType creates a new PDNType IE. func NewPDNType(pdn uint8) *IE { return NewUint8IE(PDNType, pdn) } // PDNType returns the PDNType value in uint8 if the type of IE matches. func (i *IE) PDNType() (uint8, error) { switch i.Type { case PDNType, PDNAddressAllocation: return i.ValueAsUint8() default: return 0, &InvalidTypeError{Type: i.Type} } } // MustPDNType returns PDNType in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustPDNType() uint8 { v, _ := i.PDNType() return v } ================================================ FILE: gtpv2/ie/plmn-id.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewPLMNID creates a PLMNID IE. func NewPLMNID(mcc, mnc string) *IE { encoded, err := utils.EncodePLMN(mcc, mnc) if err != nil { return nil } return New(PLMNID, 0x00, encoded) } // PLMNID returns PLMNID(MCC and MNC) in string if the type of IE matches. func (i *IE) PLMNID() (string, error) { if i.Type != PLMNID { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 3 { return "", io.ErrUnexpectedEOF } mcc, mnc, err := utils.DecodePLMN(i.Payload) if err != nil { return "", err } return mcc + mnc, nil } // MustPLMNID returns PLMNID in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustPLMNID() string { v, _ := i.PLMNID() return v } ================================================ FILE: gtpv2/ie/port-number.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewPortNumber creates a new PortNumber IE. func NewPortNumber(port uint16) *IE { return NewUint16IE(PortNumber, port) } // PortNumber returns PortNumber in uint16 if the type of IE matches. func (i *IE) PortNumber() (uint16, error) { switch i.Type { case PortNumber: return i.ValueAsUint16() default: return 0, &InvalidTypeError{Type: i.Type} } } // MustPortNumber returns PortNumber in uint16, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustPortNumber() uint16 { v, _ := i.PortNumber() return v } ================================================ FILE: gtpv2/ie/private-extension.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" ) // NewPrivateExtension creates a new PrivateExtension IE. func NewPrivateExtension(id uint16, value []byte) *IE { i := New(PrivateExtension, 0x00, make([]byte, 2+len(value))) binary.BigEndian.PutUint16(i.Payload[0:2], id) copy(i.Payload[2:], value) return i } // EnterpriseID returns EnterpriseID in uint16 if the type of IE matches. func (i *IE) EnterpriseID() (uint16, error) { if i.Type != PrivateExtension { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(i.Payload[0:2]), nil } // MustEnterpriseID returns EnterpriseID in uint16, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustEnterpriseID() uint16 { v, _ := i.EnterpriseID() return v } // PrivateExtension returns PrivateExtension value in []byte if the type of IE matches. func (i *IE) PrivateExtension() ([]byte, error) { if i.Type != PrivateExtension { return nil, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 3 { return nil, io.ErrUnexpectedEOF } return i.Payload[2:], nil } // MustPrivateExtension returns PrivateExtension in []byte, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustPrivateExtension() []byte { v, _ := i.PrivateExtension() return v } ================================================ FILE: gtpv2/ie/pti.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewProcedureTransactionID creates a new ProcedureTransactionID IE. func NewProcedureTransactionID(pti uint8) *IE { return NewUint8IE(ProcedureTransactionID, pti) } // ProcedureTransactionID returns ProcedureTransactionID in uint8 if the type of IE matches. func (i *IE) ProcedureTransactionID() (uint8, error) { if i.Type != ProcedureTransactionID { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustProcedureTransactionID returns ProcedureTransactionID in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustProcedureTransactionID() uint8 { v, _ := i.ProcedureTransactionID() return v } ================================================ FILE: gtpv2/ie/ran-nas-cause.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "fmt" "io" ) // NewRANNASCause creates a new RANNASCause IE. func NewRANNASCause(pType, cType uint8, cause []byte) *IE { v := NewRANNASCauseFields(pType, cType, cause) b, err := v.Marshal() if err != nil { return nil } return New(RANNASCause, 0x00, b) } // RANNASCause returns RANNASCause in RANNASCauseFields type if the type of IE matches. func (i *IE) RANNASCause() (*RANNASCauseFields, error) { switch i.Type { case RANNASCause: return ParseRANNASCauseFields(i.Payload) case BearerContext: ies, err := i.BearerContext() if err != nil { return nil, fmt.Errorf("failed to retrieve RANNASCause: %w", err) } for _, child := range ies { if child.Type == RANNASCause { return child.RANNASCause() } } return nil, ErrIENotFound default: return nil, &InvalidTypeError{Type: i.Type} } } // RANNASCauseFields is a set of fields in RANNASCause IE. type RANNASCauseFields struct { ProtocolType uint8 // 4-bit CauseType uint8 // 4-bit Cause []byte // format depends on ProtocolType } // NewRANNASCauseFields creates a new RANNASCauseFields. func NewRANNASCauseFields(pType, cType uint8, cause []byte) *RANNASCauseFields { return &RANNASCauseFields{ ProtocolType: pType, CauseType: cType, Cause: cause, } } // Marshal serializes RANNASCauseFields. func (f *RANNASCauseFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes RANNASCauseFields. func (f *RANNASCauseFields) MarshalTo(b []byte) error { l := len(b) if l < 1 { return io.ErrUnexpectedEOF } b[0] = ((f.ProtocolType & 0x0f) << 4) | (f.CauseType & 0x0f) if l < 1+len(f.Cause) { return io.ErrUnexpectedEOF } copy(b[1:], f.Cause) return nil } // ParseRANNASCauseFields decodes RANNASCauseFields. func ParseRANNASCauseFields(b []byte) (*RANNASCauseFields, error) { f := &RANNASCauseFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into RANNASCauseFields. func (f *RANNASCauseFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } f.ProtocolType = (b[0] & 0xf0) >> 4 f.CauseType = b[0] & 0x0f f.Cause = b[1:] return nil } // MarshalLen returns the serial length of RANNASCauseFields in int. func (f *RANNASCauseFields) MarshalLen() int { return 1 + len(f.Cause) } ================================================ FILE: gtpv2/ie/rat-type.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewRATType creates a new RATType IE. func NewRATType(rat uint8) *IE { return NewUint8IE(RATType, rat) } // RATType returns RATType in uint8 if the type of IE matches. func (i *IE) RATType() (uint8, error) { switch i.Type { case RATType: return i.ValueAsUint8() default: return 0, &InvalidTypeError{Type: i.Type} } } // MustRATType returns RATType in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustRATType() uint8 { v, _ := i.RATType() return v } ================================================ FILE: gtpv2/ie/recovery.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewRecovery creates a new Recovery IE. func NewRecovery(recovery uint8) *IE { return NewUint8IE(Recovery, recovery) } // Recovery returns Recovery value if the type of IE matches. func (i *IE) Recovery() (uint8, error) { if i.Type != Recovery { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustRecovery returns Recovery in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustRecovery() uint8 { v, _ := i.Recovery() return v } ================================================ FILE: gtpv2/ie/rfsp-index.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewRFSPIndex creates a new RFSPIndex IE. func NewRFSPIndex(idx uint8) *IE { return NewUint8IE(RFSPIndex, idx) } // RFSPIndex returns RFSPIndex in uint8 if the type of IE matches. func (i *IE) RFSPIndex() (uint8, error) { if i.Type != RFSPIndex { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustRFSPIndex returns RFSPIndex in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustRFSPIndex() uint8 { v, _ := i.RFSPIndex() return v } ================================================ FILE: gtpv2/ie/s103pdf.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "net" ) // NewS103PDNDataForwardingInfo creates a new S103PDNDataForwardingInfo IE. func NewS103PDNDataForwardingInfo(hsgwAddr string, greKey uint32, ebis ...uint8) *IE { v := NewS103PDNDataForwardingInfoFields(net.ParseIP(hsgwAddr), greKey, ebis...) b, err := v.Marshal() if err != nil { return nil } return New(S103PDNDataForwardingInfo, 0x00, b) } // NewS103PDNDataForwardingInfoNetIP creates a new S103PDNDataForwardingInfo IE. func NewS103PDNDataForwardingInfoNetIP(hsgwIP net.IP, greKey uint32, ebis ...uint8) *IE { v := NewS103PDNDataForwardingInfoFields(hsgwIP, greKey, ebis...) b, err := v.Marshal() if err != nil { return nil } return New(S103PDNDataForwardingInfo, 0x00, b) } // S103PDNDataForwardingInfo returns S103PDNDataForwardingInfo in S103PDNDataForwardingInfoFields type if the type of IE matches. func (i *IE) S103PDNDataForwardingInfo() (*S103PDNDataForwardingInfoFields, error) { switch i.Type { case S103PDNDataForwardingInfo: return ParseS103PDNDataForwardingInfoFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // S103PDNDataForwardingInfoFields is a set of fields in S103PDNDataForwardingInfo IE. type S103PDNDataForwardingInfoFields struct { HSGWAddressForForwardingLength uint8 HSGWAddressForForwarding net.IP GREKey uint32 EPSBearerIDNumber uint8 EPSBearerIDs []uint8 } // NewS103PDNDataForwardingInfoFields creates a new S103PDNDataForwardingInfoFields. func NewS103PDNDataForwardingInfoFields(hsgwIP net.IP, greKey uint32, ebis ...uint8) *S103PDNDataForwardingInfoFields { f := &S103PDNDataForwardingInfoFields{ GREKey: greKey, EPSBearerIDNumber: uint8(len(ebis)), EPSBearerIDs: ebis, } if v := hsgwIP.To4(); v != nil { f.HSGWAddressForForwardingLength = 4 f.HSGWAddressForForwarding = v return f } if v := hsgwIP.To16(); v != nil { f.HSGWAddressForForwardingLength = 16 f.HSGWAddressForForwarding = v return f } // return IE w/ "something" anyway f.HSGWAddressForForwardingLength = uint8(len(hsgwIP)) f.HSGWAddressForForwarding = hsgwIP return f } // Marshal serializes S103PDNDataForwardingInfoFields. func (f *S103PDNDataForwardingInfoFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes S103PDNDataForwardingInfoFields. func (f *S103PDNDataForwardingInfoFields) MarshalTo(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } b[0] = f.HSGWAddressForForwardingLength offset := 1 if l < offset+int(f.HSGWAddressForForwardingLength) { return io.ErrUnexpectedEOF } copy(b[offset:offset+int(f.HSGWAddressForForwardingLength)], f.HSGWAddressForForwarding) offset += int(f.HSGWAddressForForwardingLength) if l < offset+4 { return io.ErrUnexpectedEOF } binary.BigEndian.PutUint32(b[offset:offset+4], f.GREKey) offset += 4 b[offset] = f.EPSBearerIDNumber offset++ if l < offset+int(f.EPSBearerIDNumber) { return io.ErrUnexpectedEOF } for _, ebi := range f.EPSBearerIDs { b[offset] = ebi & 0x0f offset++ } return nil } // ParseS103PDNDataForwardingInfoFields decodes S103PDNDataForwardingInfoFields. func ParseS103PDNDataForwardingInfoFields(b []byte) (*S103PDNDataForwardingInfoFields, error) { f := &S103PDNDataForwardingInfoFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into S103PDNDataForwardingInfoFields. func (f *S103PDNDataForwardingInfoFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } f.HSGWAddressForForwardingLength = b[0] offset := 1 if l < offset+int(f.HSGWAddressForForwardingLength) { return io.ErrUnexpectedEOF } f.HSGWAddressForForwarding = net.IP(b[offset : offset+int(f.HSGWAddressForForwardingLength)]) offset += int(f.HSGWAddressForForwardingLength) if l < offset+4 { return io.ErrUnexpectedEOF } f.GREKey = binary.BigEndian.Uint32(b[offset : offset+4]) offset += 4 if l <= offset { return io.ErrUnexpectedEOF } f.EPSBearerIDNumber = b[offset] offset++ if l < offset+int(f.EPSBearerIDNumber) { return io.ErrUnexpectedEOF } f.EPSBearerIDs = make([]uint8, f.EPSBearerIDNumber) for n := 0; n < int(f.EPSBearerIDNumber); n++ { f.EPSBearerIDs[n] = b[offset] offset++ } return nil } // MarshalLen returns the serial length of S103PDNDataForwardingInfoFields in int. func (f *S103PDNDataForwardingInfoFields) MarshalLen() int { return 1 + int(f.HSGWAddressForForwardingLength) + 4 + 1 + int(f.EPSBearerIDNumber) } // HSGWAddress returns IP address of HSGW in string if the type of IE matches. func (i *IE) HSGWAddress() (string, error) { if i.Type != S103PDNDataForwardingInfo { return "", &InvalidTypeError{Type: i.Type} } return i.IPAddress() } // MustHSGWAddress returns HSGWAddress in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustHSGWAddress() string { v, _ := i.HSGWAddress() return v } // EBIs returns the EBIs in []uint8 if the type of IE matches. func (i *IE) EBIs() ([]uint8, error) { if i.Type != S103PDNDataForwardingInfo { return nil, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return nil, io.ErrUnexpectedEOF } var n, offset int switch i.Payload[0] { case 4: if len(i.Payload) <= 9 { return nil, io.ErrUnexpectedEOF } n = int(i.Payload[9]) offset = 10 case 16: if len(i.Payload) <= 21 { return nil, io.ErrUnexpectedEOF } n = int(i.Payload[21]) offset = 22 default: return nil, ErrMalformed } var ebis []uint8 for x := 0; x < n; x++ { if len(i.Payload) <= offset+x { break } ebis = append(ebis, i.Payload[offset+x]) } return ebis, nil } // MustEBIs returns EBIs in []uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustEBIs() []uint8 { v, _ := i.EBIs() return v } ================================================ FILE: gtpv2/ie/s1udf.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "net" ) // NewS1UDataForwarding creates a new S1UDataForwarding IE. func NewS1UDataForwarding(ebi uint8, sgwAddr string, sgwTEID uint32) *IE { v := NewS1UDataForwardingFields(ebi, net.ParseIP(sgwAddr), sgwTEID) b, err := v.Marshal() if err != nil { return nil } return New(S1UDataForwarding, 0x00, b) } // NewS1UDataForwardingNetIP creates a new S1UDataForwarding IE. func NewS1UDataForwardingNetIP(ebi uint8, sgwIP net.IP, sgwTEID uint32) *IE { v := NewS1UDataForwardingFields(ebi, sgwIP, sgwTEID) b, err := v.Marshal() if err != nil { return nil } return New(S1UDataForwarding, 0x00, b) } // S1UDataForwarding returns S1UDataForwarding in S1UDataForwardingFields type if the type of IE matches. func (i *IE) S1UDataForwarding() (*S1UDataForwardingFields, error) { switch i.Type { case S1UDataForwarding: return ParseS1UDataForwardingFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // S1UDataForwardingFields is a set of fields in S1UDataForwarding IE. type S1UDataForwardingFields struct { EPSBearerID uint8 // 4-bit ServingGWAddressLength uint8 ServingGWAddress net.IP ServingGWS1UTEID uint32 } // NewS1UDataForwardingFields creates a new S1UDataForwardingFields. func NewS1UDataForwardingFields(ebi uint8, sgwAddr net.IP, sgwTEID uint32) *S1UDataForwardingFields { f := &S1UDataForwardingFields{ EPSBearerID: ebi, ServingGWS1UTEID: sgwTEID, } if v := sgwAddr.To4(); v != nil { f.ServingGWAddressLength = 4 f.ServingGWAddress = v return f } if v := sgwAddr.To16(); v != nil { f.ServingGWAddressLength = 16 f.ServingGWAddress = v return f } // return IE w/ "something" anyway f.ServingGWAddressLength = uint8(len(sgwAddr)) f.ServingGWAddress = sgwAddr return f } // Marshal serializes S1UDataForwardingFields. func (f *S1UDataForwardingFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes S1UDataForwardingFields. func (f *S1UDataForwardingFields) MarshalTo(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } b[0] = f.EPSBearerID & 0x0f b[1] = f.ServingGWAddressLength offset := 2 if l < offset+int(f.ServingGWAddressLength) { return io.ErrUnexpectedEOF } copy(b[offset:offset+int(f.ServingGWAddressLength)], f.ServingGWAddress) offset += int(f.ServingGWAddressLength) if l < offset+4 { return io.ErrUnexpectedEOF } binary.BigEndian.PutUint32(b[offset:offset+4], f.ServingGWS1UTEID) return nil } // ParseS1UDataForwardingFields decodes S1UDataForwardingFields. func ParseS1UDataForwardingFields(b []byte) (*S1UDataForwardingFields, error) { f := &S1UDataForwardingFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into S1UDataForwardingFields. func (f *S1UDataForwardingFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } f.EPSBearerID = b[0] & 0x0f f.ServingGWAddressLength = b[1] offset := 2 if l < offset+int(f.ServingGWAddressLength) { return io.ErrUnexpectedEOF } f.ServingGWAddress = net.IP(b[offset : offset+int(f.ServingGWAddressLength)]) offset += int(f.ServingGWAddressLength) if l < offset+4 { return io.ErrUnexpectedEOF } f.ServingGWS1UTEID = binary.BigEndian.Uint32(b[offset : offset+4]) return nil } // MarshalLen returns the serial length of S1UDataForwardingFields in int. func (f *S1UDataForwardingFields) MarshalLen() int { return 2 + int(f.ServingGWAddressLength) + 4 } // SGWAddress returns IP address of SGW in string if the type of IE matches. func (i *IE) SGWAddress() (string, error) { if i.Type != S1UDataForwarding { return "", &InvalidTypeError{Type: i.Type} } return i.IPAddress() } // MustSGWAddress returns SGWAddress in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustSGWAddress() string { v, _ := i.SGWAddress() return v } ================================================ FILE: gtpv2/ie/selection-mode.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewSelectionMode creates a new SelectionMode IE. func NewSelectionMode(mode uint8) *IE { return NewUint8IE(SelectionMode, mode) } // SelectionMode returns SelectionMode value if the type of IE matches. func (i *IE) SelectionMode() (uint8, error) { if i.Type != SelectionMode { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustSelectionMode returns SelectionMode in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustSelectionMode() uint8 { v, _ := i.SelectionMode() return v } ================================================ FILE: gtpv2/ie/service-indicator.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewServiceIndicator creates a new ServiceIndicator IE. func NewServiceIndicator(ind uint8) *IE { return NewUint8IE(ServiceIndicator, ind) } // ServiceIndicator returns ServiceIndicator in uint8 if the type of IE matches. func (i *IE) ServiceIndicator() (uint8, error) { if i.Type != ServiceIndicator { return 0, &InvalidTypeError{Type: i.Type} } return i.ValueAsUint8() } // MustServiceIndicator returns ServiceIndicator in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustServiceIndicator() uint8 { v, _ := i.ServiceIndicator() return v } ================================================ FILE: gtpv2/ie/serving-nw.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewServingNetwork creates a ServingNetwork IE. func NewServingNetwork(mcc, mnc string) *IE { encoded, err := utils.EncodePLMN(mcc, mnc) if err != nil { return nil } return New(ServingNetwork, 0x00, encoded) } // ServingNetwork returns ServingNetwork(MCC and MNC) in string if the type of IE matches. func (i *IE) ServingNetwork() (string, error) { if i.Type != ServingNetwork { return "", &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 3 { return "", io.ErrUnexpectedEOF } mcc, mnc, err := utils.DecodePLMN(i.Payload) if err != nil { return "", err } return mcc + mnc, nil } // MustServingNetwork returns ServingNetwork in string, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustServingNetwork() string { v, _ := i.ServingNetwork() return v } ================================================ FILE: gtpv2/ie/tad.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewTrafficAggregateDescription creates a new TrafficAggregateDescription IE. // // Custom constructors for each operation code are available, which does not require // unnecessary parameters. func NewTrafficAggregateDescription(op uint8, filters []*TFTPacketFilter, ids []uint8, params []*TFTParameter) *IE { v := NewTrafficFlowTemplate(op, filters, ids, params) b, err := v.Marshal() if err != nil { return nil } return New(TrafficAggregateDescription, 0x00, b) } // NewTrafficAggregateDescriptionCreateNewTFT creates a new TrafficAggregateDescription IE with opcode=CreateNewTFT. func NewTrafficAggregateDescriptionCreateNewTFT(filters []*TFTPacketFilter, params []*TFTParameter) *IE { return NewTrafficAggregateDescription(TFTOpCreateNewTFT, filters, nil, params) } // NewTrafficAggregateDescriptionAddPacketFilters creates a new TrafficAggregateDescription IE with opcode=AddPacketFiltersToExistingTFT. func NewTrafficAggregateDescriptionAddPacketFilters(filters []*TFTPacketFilter, params []*TFTParameter) *IE { return NewTrafficAggregateDescription(TFTOpAddPacketFiltersToExistingTFT, filters, nil, params) } // NewTrafficAggregateDescriptionReplacePacketFilters creates a new TrafficAggregateDescription IE with opcode=ReplacePacketFiltersInExistingTFT. func NewTrafficAggregateDescriptionReplacePacketFilters(filters []*TFTPacketFilter, params []*TFTParameter) *IE { return NewTrafficAggregateDescription(TFTOpReplacePacketFiltersInExistingTFT, filters, nil, params) } // NewTrafficAggregateDescriptionDeletePacketFilters creates a new TrafficAggregateDescription IE with opcode=DeletePacketFiltersFromExistingTFT. func NewTrafficAggregateDescriptionDeletePacketFilters(ids []uint8, params ...*TFTParameter) *IE { return NewTrafficAggregateDescription(TFTOpDeletePacketFiltersFromExistingTFT, nil, ids, params) } // NewTrafficAggregateDescriptionDeleteExistingTFT creates a new TrafficAggregateDescription IE with opcode=DeleteExistingTFT. func NewTrafficAggregateDescriptionDeleteExistingTFT(params ...*TFTParameter) *IE { return NewTrafficAggregateDescription(TFTOpDeleteExistingTFT, nil, nil, params) } // NewTrafficAggregateDescriptionNoTFTOperation creates a new TrafficAggregateDescription IE with opcode=NoTFTOperation. func NewTrafficAggregateDescriptionNoTFTOperation(params ...*TFTParameter) *IE { return NewTrafficAggregateDescription(TFTOpNoTFTOperation, nil, nil, params) } // TrafficAggregateDescription returns TrafficAggregateDescription in TrafficFlowTemplate type if the type of IE matches. func (i *IE) TrafficAggregateDescription() (*TrafficFlowTemplate, error) { return i.TrafficFlowTemplate() } ================================================ FILE: gtpv2/ie/tft.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "fmt" "io" "net" "github.com/wmnsk/go-gtp/utils" ) // TrafficFlowTemplate returns TrafficFlowTemplate struct if the type of IE matches. func (i *IE) TrafficFlowTemplate() (*TrafficFlowTemplate, error) { switch i.Type { case BearerTFT, TrafficAggregateDescription: return ParseTrafficFlowTemplate(i.Payload) case BearerContext: ies, err := i.BearerContext() if err != nil { return nil, fmt.Errorf("failed to retrieve BearerContext: %w", err) } for _, child := range ies { if child.Type == BearerTFT { return child.TrafficFlowTemplate() } } return nil, ErrIENotFound default: return nil, &InvalidTypeError{Type: i.Type} } } // TrafficFlowTemplate is a set of fields in BearerTFT IE. type TrafficFlowTemplate struct { OperationCode uint8 PacketFilters []*TFTPacketFilter PacketFilterIdentifiers []uint8 Parameters []*TFTParameter } // NewTrafficFlowTemplate creates a new TrafficFlowTemplate. func NewTrafficFlowTemplate(op uint8, filters []*TFTPacketFilter, ids []uint8, params []*TFTParameter) *TrafficFlowTemplate { var fs []*TFTPacketFilter for _, f := range filters { if f != nil { fs = append(fs, f) } } var ps []*TFTParameter for _, p := range params { if p != nil { ps = append(ps, p) } } return &TrafficFlowTemplate{ OperationCode: op, PacketFilters: fs, PacketFilterIdentifiers: ids, Parameters: ps, } } // Marshal serializes TrafficFlowTemplate. func (f *TrafficFlowTemplate) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes TrafficFlowTemplate. func (f *TrafficFlowTemplate) MarshalTo(b []byte) error { if len(b) < 1 { return io.ErrUnexpectedEOF } // 000. .... = TFT operation code // ...0 .... = E bit // .... 0000 = Number of packet filters op := (f.OperationCode & 0b111) << 5 e := uint8(len(f.Parameters)*1&0b1) << 4 pf := len(f.PacketFilters) if f.OperationCode == TFTOpDeletePacketFiltersFromExistingTFT { pf = len(f.PacketFilterIdentifiers) } b[0] = op | e | uint8(pf&0b1111) offset := 1 switch f.OperationCode { case TFTOpCreateNewTFT, TFTOpAddPacketFiltersToExistingTFT, TFTOpReplacePacketFiltersInExistingTFT: for _, filter := range f.PacketFilters { if filter == nil { continue } if err := filter.MarshalTo(b[offset:]); err != nil { return fmt.Errorf("failed to marshal Packet Filter: %w", err) } offset += filter.MarshalLen() } case TFTOpDeletePacketFiltersFromExistingTFT: copy(b[offset:offset+pf], f.PacketFilterIdentifiers) offset += pf } for _, param := range f.Parameters { if param == nil { continue } if err := param.MarshalTo(b[offset:]); err != nil { return fmt.Errorf("failed to marshal Parameter: %w", err) } offset += param.MarshalLen() } return nil } // ParseTrafficFlowTemplate decodes TrafficFlowTemplate. func ParseTrafficFlowTemplate(b []byte) (*TrafficFlowTemplate, error) { f := &TrafficFlowTemplate{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into TrafficFlowTemplate. func (f *TrafficFlowTemplate) UnmarshalBinary(b []byte) error { if len(b) < 1 { return io.ErrUnexpectedEOF } f.OperationCode = b[0] >> 5 hasParams := (b[0] >> 4 & 0b1) == 1 filterLen := int(b[0] & 0b1111) offset := 1 switch f.OperationCode { case TFTOpCreateNewTFT, TFTOpAddPacketFiltersToExistingTFT, TFTOpReplacePacketFiltersInExistingTFT: f.PacketFilters = []*TFTPacketFilter{} for i := 0; i < filterLen; i++ { filter, err := ParseTFTPacketFilter(b[offset:]) if err != nil { return fmt.Errorf("failed to parse Packet Filter: %w", err) } f.PacketFilters = append(f.PacketFilters, filter) offset += filter.MarshalLen() } case TFTOpDeletePacketFiltersFromExistingTFT: if len(b) < offset+filterLen { return io.ErrUnexpectedEOF } f.PacketFilterIdentifiers = b[offset : offset+filterLen] offset += filterLen } if hasParams { params, err := ParseMultiTFTParameters(b[offset:]) if err != nil { return fmt.Errorf("failed to parse Parameters: %w", err) } f.Parameters = params } return nil } // MarshalLen returns the serial length of TrafficFlowTemplate in int. func (f *TrafficFlowTemplate) MarshalLen() int { l := 1 for _, filter := range f.PacketFilters { if filter == nil { continue } l += filter.MarshalLen() } l += len(f.PacketFilterIdentifiers) for _, param := range f.Parameters { if param == nil { continue } l += param.MarshalLen() } return l } // TFT Packet Filter Identifier definitions. const ( TFTPFPreRel7TFTFilter uint8 = 0 TFTPFDownlinkOnly uint8 = 1 TFTPFUplinkOnly uint8 = 2 TFTPFBidirectional uint8 = 3 ) // TFTPacketFilter represents a PacketFilter in TFT. type TFTPacketFilter struct { Direction uint8 Identifier uint8 EvaluationPrecedence uint8 Length uint8 Components []*TFTPFComponent } // NewTFTPacketFilter creates a new TFTPacketFilter. func NewTFTPacketFilter(dir, id, precedence uint8, comps ...*TFTPFComponent) *TFTPacketFilter { pf := &TFTPacketFilter{ Direction: dir, Identifier: id, EvaluationPrecedence: precedence, Components: comps, } pf.SetLength() return pf } // Marshal serializes TFTPacketFilter. func (p *TFTPacketFilter) Marshal() ([]byte, error) { b := make([]byte, p.MarshalLen()) if err := p.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes TFTPacketFilter into b. func (p *TFTPacketFilter) MarshalTo(b []byte) error { l := len(b) if l < 3 { return io.ErrUnexpectedEOF } b[0] = (p.Direction&0b11)<<4 | p.Identifier&0b1111 b[1] = p.EvaluationPrecedence b[2] = p.Length offset := 3 for _, comp := range p.Components { n := comp.MarshalLen() if l < offset+n { return io.ErrUnexpectedEOF } if err := comp.MarshalTo(b[offset : offset+n]); err != nil { return err } offset += n } return nil } // ParseTFTPacketFilter decodes TFTPacketFilter. func ParseTFTPacketFilter(b []byte) (*TFTPacketFilter, error) { p := &TFTPacketFilter{} if err := p.UnmarshalBinary(b); err != nil { return nil, err } return p, nil } // UnmarshalBinary decodes given bytes into TFTPacketFilter. func (p *TFTPacketFilter) UnmarshalBinary(b []byte) error { l := len(b) if l < 3 { return io.ErrUnexpectedEOF } p.Direction = (b[0] >> 4) & 0b11 p.Identifier = b[0] & 0b1111 p.EvaluationPrecedence = b[1] p.Length = b[2] offset := 3 n := offset + int(p.Length) if l < n { return io.ErrUnexpectedEOF } comps, err := ParseMultiTFTPFComponent(b[offset:n]) if err != nil { return err } p.Components = comps return nil } // SetLength sets the length in TFTPacketFilter. func (p *TFTPacketFilter) SetLength() { l := 0 for _, comp := range p.Components { l += comp.MarshalLen() } p.Length = uint8(l) } // MarshalLen returns the serial length of TFTPacketFilter in int. func (p *TFTPacketFilter) MarshalLen() int { l := 3 for _, comp := range p.Components { l += comp.MarshalLen() } return l } // Packet Filter Component Type definitions. const ( PFCompIPv4RemoteAddress uint8 = 0b00010000 PFCompIPv4LocalAddress uint8 = 0b00010001 PFCompIPv6RemoteAddress uint8 = 0b00100000 PFCompIPv6RemoteAddressPrefixLength uint8 = 0b00100001 PFCompIPv6LocalAddressPrefixLength uint8 = 0b00100011 PFCompProtocolIdentifierNextHeader uint8 = 0b00110000 PFCompSingleLocalPort uint8 = 0b01000000 PFCompLocalPortRange uint8 = 0b01000001 PFCompSingleRemotePort uint8 = 0b01010000 PFCompRemotePortRange uint8 = 0b01010001 PFCompSecurityParameterIndex uint8 = 0b01100000 PFCompTypeOfServiceTrafficClass uint8 = 0b01110000 PFCompFlowLabel uint8 = 0b10000000 PFCompDestinationMACAddress uint8 = 0b10000001 PFCompSourceMACAddress uint8 = 0b10000010 PFCompDot1QCTAGVID uint8 = 0b10000011 PFCompDot1QSTAGVID uint8 = 0b10000100 PFCompDot1QCTAGPCPDEI uint8 = 0b10000101 PFCompDot1QSTAGPCPDEI uint8 = 0b10000110 PFCompEthertype uint8 = 0b10000111 ) // TFTPFComponent represents a component in Packet Fileter in TFT. type TFTPFComponent struct { Type uint8 Contents []byte } // NewTFTPFComponent creates a new TFTPFComponent. func NewTFTPFComponent(t uint8, contents []byte) *TFTPFComponent { return &TFTPFComponent{ Type: t, Contents: contents, } } // NewTFTPFComponentIPv4RemoteAddress creates a new TFTPFComponent of type IPv4RemoteAddress. func NewTFTPFComponentIPv4RemoteAddress(ip net.IP, mask net.IPMask) *TFTPFComponent { return NewTFTPFComponent(PFCompIPv4RemoteAddress, append(ip.To4(), mask...)) } // NewTFTPFComponentIPv4LocalAddress creates a new TFTPFComponent of type IPv4LocalAddress. func NewTFTPFComponentIPv4LocalAddress(ip net.IP, mask net.IPMask) *TFTPFComponent { return NewTFTPFComponent(PFCompIPv4LocalAddress, append(ip.To4(), mask...)) } // NewTFTPFComponentIPv6RemoteAddress creates a new TFTPFComponent of type IPv6RemoteAddress. func NewTFTPFComponentIPv6RemoteAddress(ip net.IP, mask net.IPMask) *TFTPFComponent { return NewTFTPFComponent(PFCompIPv6RemoteAddress, append(ip.To16(), mask...)) } // NewTFTPFComponentIPv6RemoteAddressPrefixLength creates a new TFTPFComponent of type IPv6RemoteAddressPrefixLength. func NewTFTPFComponentIPv6RemoteAddressPrefixLength(ip net.IP, prefix uint8) *TFTPFComponent { return NewTFTPFComponent(PFCompIPv6RemoteAddressPrefixLength, append(ip.To16(), prefix)) } // NewTFTPFComponentIPv6LocalAddressPrefixLength creates a new TFTPFComponent of type IPv6LocalAddressPrefixLength. func NewTFTPFComponentIPv6LocalAddressPrefixLength(ip net.IP, prefix uint8) *TFTPFComponent { return NewTFTPFComponent(PFCompIPv6LocalAddressPrefixLength, append(ip.To16(), prefix)) } // NewTFTPFComponentProtocolIdentifierNextHeader creates a new TFTPFComponent of type ProtocolIdentifierNextHeader. func NewTFTPFComponentProtocolIdentifierNextHeader(id uint8) *TFTPFComponent { return NewTFTPFComponent(PFCompProtocolIdentifierNextHeader, []byte{id}) } // NewTFTPFComponentSingleLocalPort creates a new TFTPFComponent of type SingleLocalPort. func NewTFTPFComponentSingleLocalPort(port uint16) *TFTPFComponent { b := make([]byte, 2) binary.BigEndian.PutUint16(b, port) return NewTFTPFComponent(PFCompSingleLocalPort, b) } // NewTFTPFComponentLocalPortRange creates a new TFTPFComponent of type LocalPortRange. func NewTFTPFComponentLocalPortRange(low, high uint16) *TFTPFComponent { b := make([]byte, 4) binary.BigEndian.PutUint16(b[0:2], low) binary.BigEndian.PutUint16(b[2:4], high) return NewTFTPFComponent(PFCompLocalPortRange, b) } // NewTFTPFComponentSingleRemotePort creates a new TFTPFComponent of type SingleRemotePort. func NewTFTPFComponentSingleRemotePort(port uint16) *TFTPFComponent { b := make([]byte, 2) binary.BigEndian.PutUint16(b, port) return NewTFTPFComponent(PFCompSingleRemotePort, b) } // NewTFTPFComponentRemotePortRange creates a new TFTPFComponent of type RemotePortRange. func NewTFTPFComponentRemotePortRange(low, high uint16) *TFTPFComponent { b := make([]byte, 4) binary.BigEndian.PutUint16(b[0:2], low) binary.BigEndian.PutUint16(b[2:4], high) return NewTFTPFComponent(PFCompRemotePortRange, b) } // NewTFTPFComponentSecurityParameterIndex creates a new TFTPFComponent of type SecurityParameterIndex. func NewTFTPFComponentSecurityParameterIndex(idx uint32) *TFTPFComponent { b := make([]byte, 4) binary.BigEndian.PutUint32(b, idx) return NewTFTPFComponent(PFCompSecurityParameterIndex, b) } // NewTFTPFComponentTypeOfServiceTrafficClass creates a new TFTPFComponent of type TypeOfServiceTrafficClass. func NewTFTPFComponentTypeOfServiceTrafficClass(class, mask uint8) *TFTPFComponent { return NewTFTPFComponent(PFCompTypeOfServiceTrafficClass, []byte{class, mask}) } // NewTFTPFComponentFlowLabel creates a new TFTPFComponent of type FlowLabel. func NewTFTPFComponentFlowLabel(label uint32) *TFTPFComponent { return NewTFTPFComponent(PFCompFlowLabel, utils.Uint32To24(label)) } // NewTFTPFComponentDestinationMACAddress creates a new TFTPFComponent of type DestinationMACAddress. func NewTFTPFComponentDestinationMACAddress(mac net.HardwareAddr) *TFTPFComponent { return NewTFTPFComponent(PFCompDestinationMACAddress, mac) } // NewTFTPFComponentSourceMACAddress creates a new TFTPFComponent of type SourceMACAddress. func NewTFTPFComponentSourceMACAddress(mac net.HardwareAddr) *TFTPFComponent { return NewTFTPFComponent(PFCompSourceMACAddress, mac) } // NewTFTPFComponentDot1QCTAGVID creates a new TFTPFComponent of type Dot1QCTAGVID. func NewTFTPFComponentDot1QCTAGVID(vid uint16) *TFTPFComponent { b := make([]byte, 2) binary.BigEndian.PutUint16(b, vid) return NewTFTPFComponent(PFCompDot1QCTAGVID, b) } // NewTFTPFComponentDot1QSTAGVID creates a new TFTPFComponent of type Dot1QSTAGVID. func NewTFTPFComponentDot1QSTAGVID(vid uint16) *TFTPFComponent { b := make([]byte, 2) binary.BigEndian.PutUint16(b, vid) return NewTFTPFComponent(PFCompDot1QSTAGVID, b) } // NewTFTPFComponentDot1QCTAGPCPDEI creates a new TFTPFComponent of type Dot1QCTAGPCPDEI. func NewTFTPFComponentDot1QCTAGPCPDEI(pcpdei uint8) *TFTPFComponent { return NewTFTPFComponent(PFCompDot1QCTAGPCPDEI, []byte{pcpdei}) } // NewTFTPFComponentDot1QSTAGPCPDEI creates a new TFTPFComponent of type Dot1QSTAGPCPDEI. func NewTFTPFComponentDot1QSTAGPCPDEI(pcpdei uint8) *TFTPFComponent { return NewTFTPFComponent(PFCompDot1QSTAGPCPDEI, []byte{pcpdei}) } // NewTFTPFComponentEthertype creates a new TFTPFComponent of type Ethertype. func NewTFTPFComponentEthertype(etype uint16) *TFTPFComponent { b := make([]byte, 2) binary.BigEndian.PutUint16(b, etype) return NewTFTPFComponent(PFCompEthertype, b) } // IPv4RemoteAddress returns IPv4RemoteAddress in net.IPNet if the type of component matches. func (c *TFTPFComponent) IPv4RemoteAddress() (*net.IPNet, error) { if c.Type != PFCompIPv4RemoteAddress { return nil, ErrInvalidType } if len(c.Contents) < 8 { return nil, io.ErrUnexpectedEOF } return &net.IPNet{IP: c.Contents[:4], Mask: c.Contents[4:8]}, nil } // IPv4LocalAddress returns IPv4LocalAddress in net.IPNet if the type of component matches. func (c *TFTPFComponent) IPv4LocalAddress() (*net.IPNet, error) { if c.Type != PFCompIPv4LocalAddress { return nil, ErrInvalidType } if len(c.Contents) < 8 { return nil, io.ErrUnexpectedEOF } return &net.IPNet{IP: c.Contents[:4], Mask: c.Contents[4:8]}, nil } // IPv6RemoteAddress returns IPv6RemoteAddress in net.IPNet if the type of component matches. func (c *TFTPFComponent) IPv6RemoteAddress() (*net.IPNet, error) { if c.Type != PFCompIPv6RemoteAddress { return nil, ErrInvalidType } if len(c.Contents) < 32 { return nil, io.ErrUnexpectedEOF } return &net.IPNet{IP: c.Contents[:16], Mask: c.Contents[17:32]}, nil } // IPv6RemoteAddressPrefixLength returns IPv6RemoteAddressPrefixLength in *net.IPNet // if the type of component matches. func (c *TFTPFComponent) IPv6RemoteAddressPrefixLength() (*net.IPNet, error) { if c.Type != PFCompIPv6RemoteAddressPrefixLength { return nil, ErrInvalidType } if len(c.Contents) < 17 { return nil, io.ErrUnexpectedEOF } ipnet := &net.IPNet{ IP: net.IP(c.Contents[:16]), Mask: net.CIDRMask(int(c.Contents[17]), 128), } return ipnet, nil } // IPv6LocalAddressPrefixLength returns IPv6LocalAddressPrefixLength in *net.IPNet // if the type of component matches. func (c *TFTPFComponent) IPv6LocalAddressPrefixLength() (*net.IPNet, error) { if c.Type != PFCompIPv6LocalAddressPrefixLength { return nil, ErrInvalidType } if len(c.Contents) < 17 { return nil, io.ErrUnexpectedEOF } ipnet := &net.IPNet{ IP: net.IP(c.Contents[:16]), Mask: net.CIDRMask(int(c.Contents[17]), 128), } return ipnet, nil } // ProtocolIdentifierNextHeader returns ProtocolIdentifierNextHeader in uint8 // if the type of component matches. func (c *TFTPFComponent) ProtocolIdentifierNextHeader() (uint8, error) { if c.Type != PFCompProtocolIdentifierNextHeader { return 0, ErrInvalidType } if len(c.Contents) < 1 { return 0, io.ErrUnexpectedEOF } return c.Contents[0], nil } // SingleLocalPort returns SingleLocalPort in uint16 if the type of component matches. func (c *TFTPFComponent) SingleLocalPort() (uint16, error) { if c.Type != PFCompSingleLocalPort { return 0, ErrInvalidType } if len(c.Contents) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(c.Contents[:2]), nil } // LocalPortRange returns LocalPortRange in two uint16(low, high) if the type of // component matches. func (c *TFTPFComponent) LocalPortRange() (uint16, uint16, error) { if c.Type != PFCompLocalPortRange { return 0, 0, ErrInvalidType } if len(c.Contents) < 4 { return 0, 0, io.ErrUnexpectedEOF } low := binary.BigEndian.Uint16(c.Contents[0:2]) high := binary.BigEndian.Uint16(c.Contents[2:4]) return low, high, nil } // SingleRemotePort returns SingleRemotePort in uint16 if the type of component matches. func (c *TFTPFComponent) SingleRemotePort() (uint16, error) { if c.Type != PFCompSingleRemotePort { return 0, ErrInvalidType } if len(c.Contents) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(c.Contents[:2]), nil } // RemotePortRange returns RemotePortRange in two uint16(low, high) if the type of // component matches. func (c *TFTPFComponent) RemotePortRange() (uint16, uint16, error) { if c.Type != PFCompRemotePortRange { return 0, 0, ErrInvalidType } if len(c.Contents) < 4 { return 0, 0, io.ErrUnexpectedEOF } low := binary.BigEndian.Uint16(c.Contents[0:2]) high := binary.BigEndian.Uint16(c.Contents[2:4]) return low, high, nil } // SecurityParameterIndex returns SecurityParameterIndex in uint32 if the type of // component matches. func (c *TFTPFComponent) SecurityParameterIndex() (uint32, error) { if c.Type != PFCompSecurityParameterIndex { return 0, ErrInvalidType } if len(c.Contents) < 4 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint32(c.Contents[:4]), nil } // TypeOfServiceTrafficClass returns TypeOfServiceTrafficClass in two uint8 // (class, mask) if the type of component matches. func (c *TFTPFComponent) TypeOfServiceTrafficClass() (uint8, uint8, error) { if c.Type != PFCompTypeOfServiceTrafficClass { return 0, 0, ErrInvalidType } if len(c.Contents) < 2 { return 0, 0, io.ErrUnexpectedEOF } return c.Contents[0], c.Contents[1], nil } // FlowLabel returns FlowLabel in uint32 if the type of component matches. func (c *TFTPFComponent) FlowLabel() (uint32, error) { if c.Type != PFCompFlowLabel { return 0, ErrInvalidType } if len(c.Contents) < 3 { return 0, io.ErrUnexpectedEOF } return utils.Uint24To32(c.Contents[:3]), nil } // DestinationMACAddress returns DestinationMACAddress in net.HardwareAddr if // the type of component matches. func (c *TFTPFComponent) DestinationMACAddress() (net.HardwareAddr, error) { if c.Type != PFCompDestinationMACAddress { return nil, ErrInvalidType } if len(c.Contents) < 6 { return nil, io.ErrUnexpectedEOF } return net.HardwareAddr(c.Contents[:6]), nil } // SourceMACAddress returns SourceMACAddress in net.HardwareAddr if the type of // component matches. func (c *TFTPFComponent) SourceMACAddress() (net.HardwareAddr, error) { if c.Type != PFCompSourceMACAddress { return nil, ErrInvalidType } if len(c.Contents) < 6 { return nil, io.ErrUnexpectedEOF } return net.HardwareAddr(c.Contents[:6]), nil } // Dot1QCTAGVID returns Dot1QCTAGVID in uint16 if the type of component matches. func (c *TFTPFComponent) Dot1QCTAGVID() (uint16, error) { if c.Type != PFCompDot1QCTAGVID { return 0, ErrInvalidType } if len(c.Contents) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(c.Contents[:2]), nil } // Dot1QSTAGVID returns Dot1QSTAGVID in uint16 if the type of component matches. func (c *TFTPFComponent) Dot1QSTAGVID() (uint16, error) { if c.Type != PFCompDot1QSTAGVID { return 0, ErrInvalidType } if len(c.Contents) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(c.Contents[:2]), nil } // Dot1QCTAGPCPDEI returns Dot1QCTAGPCPDEI in uint8 if the type of component matches. func (c *TFTPFComponent) Dot1QCTAGPCPDEI() (uint8, error) { if c.Type != PFCompDot1QCTAGPCPDEI { return 0, ErrInvalidType } if len(c.Contents) < 1 { return 0, io.ErrUnexpectedEOF } return c.Contents[0], nil } // Dot1QSTAGPCPDEI returns Dot1QSTAGPCPDEI in uint8 if the type of component matches. func (c *TFTPFComponent) Dot1QSTAGPCPDEI() (uint8, error) { if c.Type != PFCompDot1QSTAGPCPDEI { return 0, ErrInvalidType } if len(c.Contents) < 1 { return 0, io.ErrUnexpectedEOF } return c.Contents[0], nil } // Ethertype returns Ethertype in uint16 if the type of component matches. func (c *TFTPFComponent) Ethertype() (uint16, error) { if c.Type != PFCompEthertype { return 0, ErrInvalidType } if len(c.Contents) < 2 { return 0, io.ErrUnexpectedEOF } return binary.BigEndian.Uint16(c.Contents[:2]), nil } // Marshal serializes TFTPFComponent. func (c *TFTPFComponent) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes TFTPFComponent into b. func (c *TFTPFComponent) MarshalTo(b []byte) error { if len(b) < 1+len(c.Contents) { return io.ErrUnexpectedEOF } b[0] = c.Type copy(b[1:1+len(c.Contents)], c.Contents) return nil } // ParseTFTPFComponent decodes TFTPFComponent. func ParseTFTPFComponent(b []byte) (*TFTPFComponent, error) { c := &TFTPFComponent{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // ParseMultiTFTPFComponent decodes TFTPFComponent. func ParseMultiTFTPFComponent(b []byte) ([]*TFTPFComponent, error) { var comps []*TFTPFComponent for { if len(b) == 0 { break } i, err := ParseTFTPFComponent(b) if err != nil { return nil, err } comps = append(comps, i) b = b[i.MarshalLen():] } return comps, nil } // UnmarshalBinary decodes given bytes into TFTPFComponent. func (c *TFTPFComponent) UnmarshalBinary(b []byte) error { l := len(b) if l < 1 { return io.ErrUnexpectedEOF } c.Type = b[0] n := 0 switch c.Type { case PFCompIPv4RemoteAddress, PFCompIPv4LocalAddress: n = 8 case PFCompIPv6RemoteAddress: n = 32 case PFCompIPv6RemoteAddressPrefixLength, PFCompIPv6LocalAddressPrefixLength: n = 17 case PFCompProtocolIdentifierNextHeader: n = 1 case PFCompSingleLocalPort, PFCompSingleRemotePort: n = 2 case PFCompLocalPortRange, PFCompRemotePortRange: n = 4 case PFCompSecurityParameterIndex: n = 4 case PFCompTypeOfServiceTrafficClass: n = 2 case PFCompFlowLabel: n = 3 case PFCompDestinationMACAddress, PFCompSourceMACAddress: n = 6 case PFCompDot1QCTAGVID, PFCompDot1QSTAGVID: n = 2 case PFCompDot1QCTAGPCPDEI, PFCompDot1QSTAGPCPDEI: n = 1 case PFCompEthertype: n = 2 } if l < 1+n { return io.ErrUnexpectedEOF } c.Contents = b[1 : 1+n] return nil } // MarshalLen returns the serial length of TFTPFComponent in int. func (c *TFTPFComponent) MarshalLen() int { return 1 + len(c.Contents) } // TFT Parameter Identifier definitions. const ( TFTParamIDAuthorizationToken uint8 = 1 TFTParamIDFlowIdentifier uint8 = 2 TFTParamIDPacketFileterIdentifier uint8 = 3 ) // TFTParameter represents a Parameter in TFT. type TFTParameter struct { Identifier uint8 Length uint8 Contents []byte } // NewTFTParameter creates a new TFTParameter. func NewTFTParameter(id uint8, contents []byte) *TFTParameter { return &TFTParameter{ Identifier: id, Length: uint8(len(contents)), Contents: contents, } } // Marshal serializes TFTParameter. func (p *TFTParameter) Marshal() ([]byte, error) { b := make([]byte, p.MarshalLen()) if err := p.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes TFTParameter into b. func (p *TFTParameter) MarshalTo(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } b[0] = p.Identifier b[1] = p.Length offset := 2 if l < offset+len(p.Contents) { return io.ErrUnexpectedEOF } copy(b[offset:offset+len(p.Contents)], p.Contents) return nil } // ParseTFTParameter decodes TFTParameter. func ParseTFTParameter(b []byte) (*TFTParameter, error) { p := &TFTParameter{} if err := p.UnmarshalBinary(b); err != nil { return nil, err } return p, nil } // ParseMultiTFTParameters decodes TFTParameter. func ParseMultiTFTParameters(b []byte) ([]*TFTParameter, error) { var params []*TFTParameter for { if len(b) == 0 { break } i, err := ParseTFTParameter(b) if err != nil { return nil, err } params = append(params, i) b = b[i.MarshalLen():] } return params, nil } // UnmarshalBinary decodes given bytes into TFTParameter. func (p *TFTParameter) UnmarshalBinary(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } p.Identifier = b[0] p.Length = b[1] if l < 2+int(p.Length) { return io.ErrUnexpectedEOF } p.Contents = b[2 : 2+int(p.Length)] return nil } // SetLength sets the length in TFTParameter. func (p *TFTParameter) SetLength() { p.Length = uint8(len(p.Contents)) } // MarshalLen returns the serial length of TFTParameter in int. func (p *TFTParameter) MarshalLen() int { return 2 + len(p.Contents) } ================================================ FILE: gtpv2/ie/tft_test.go ================================================ package ie_test import ( "net" "testing" "github.com/google/go-cmp/cmp" "github.com/wmnsk/go-gtp/gtpv2/ie" ) func TestTrafficFlowTemplate(t *testing.T) { cases := []struct { description string serialized []byte filters []*ie.TFTPacketFilter ids []uint8 params []*ie.TFTParameter }{ { "TFTOpCreateNewTFT/NoParams", []byte{ 0x54, 0x00, 0x9d, 0x00, 0x24, 0x01, 0x00, 0x57, 0x10, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x11, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x23, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x12, 0x00, 0x12, 0x30, 0x01, 0x40, 0x08, 0x68, 0x50, 0x08, 0x4b, 0x41, 0x08, 0x4b, 0x08, 0x68, 0x51, 0x08, 0x68, 0x08, 0x4b, 0x23, 0x00, 0x1a, 0x60, 0xde, 0xad, 0xbe, 0xef, 0x70, 0x01, 0x02, 0x80, 0x01, 0x11, 0x11, 0x81, 0x12, 0x34, 0x56, 0x78, 0x90, 0x01, 0x82, 0x12, 0x34, 0x56, 0x78, 0x90, 0x02, 0x34, 0x00, 0x0d, 0x83, 0x01, 0x11, 0x84, 0x02, 0x22, 0x85, 0x03, 0x86, 0x05, 0x87, 0x08, 0x00, }, []*ie.TFTPacketFilter{ ie.NewTFTPacketFilter( ie.TFTPFPreRel7TFTFilter, 1, 0, ie.NewTFTPFComponentIPv4RemoteAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv4LocalAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv6RemoteAddress( net.ParseIP("2001::1"), net.CIDRMask(64, 128), ), ie.NewTFTPFComponentIPv6RemoteAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ie.NewTFTPFComponentIPv6LocalAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ), ie.NewTFTPacketFilter( ie.TFTPFDownlinkOnly, 2, 0, ie.NewTFTPFComponentProtocolIdentifierNextHeader(1), ie.NewTFTPFComponentSingleLocalPort(2152), ie.NewTFTPFComponentSingleRemotePort(2123), ie.NewTFTPFComponentLocalPortRange(2123, 2152), ie.NewTFTPFComponentRemotePortRange(2152, 2123), ), ie.NewTFTPacketFilter( ie.TFTPFUplinkOnly, 3, 0, ie.NewTFTPFComponentSecurityParameterIndex(0xdeadbeef), ie.NewTFTPFComponentTypeOfServiceTrafficClass(1, 2), ie.NewTFTPFComponentFlowLabel(0x00011111), ie.NewTFTPFComponentDestinationMACAddress(mac1), ie.NewTFTPFComponentSourceMACAddress(mac2), ), ie.NewTFTPacketFilter( ie.TFTPFBidirectional, 4, 0, ie.NewTFTPFComponentDot1QCTAGVID(0x0111), ie.NewTFTPFComponentDot1QSTAGVID(0x0222), ie.NewTFTPFComponentDot1QCTAGPCPDEI(3), ie.NewTFTPFComponentDot1QSTAGPCPDEI(5), ie.NewTFTPFComponentEthertype(0x0800), ), }, nil, nil, }, { "TFTOpCreateNewTFT/WithParams", []byte{ 0x54, 0x00, 0xaf, 0x00, 0x34, 0x01, 0x00, 0x57, 0x10, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x11, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x23, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x12, 0x00, 0x12, 0x30, 0x01, 0x40, 0x08, 0x68, 0x50, 0x08, 0x4b, 0x41, 0x08, 0x4b, 0x08, 0x68, 0x51, 0x08, 0x68, 0x08, 0x4b, 0x23, 0x00, 0x1a, 0x60, 0xde, 0xad, 0xbe, 0xef, 0x70, 0x01, 0x02, 0x80, 0x01, 0x11, 0x11, 0x81, 0x12, 0x34, 0x56, 0x78, 0x90, 0x01, 0x82, 0x12, 0x34, 0x56, 0x78, 0x90, 0x02, 0x34, 0x00, 0x0d, 0x83, 0x01, 0x11, 0x84, 0x02, 0x22, 0x85, 0x03, 0x86, 0x05, 0x87, 0x08, 0x00, 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x02, 0x04, 0x11, 0x11, 0x22, 0x22, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, }, []*ie.TFTPacketFilter{ ie.NewTFTPacketFilter( ie.TFTPFPreRel7TFTFilter, 1, 0, ie.NewTFTPFComponentIPv4RemoteAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv4LocalAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv6RemoteAddress( net.ParseIP("2001::1"), net.CIDRMask(64, 128), ), ie.NewTFTPFComponentIPv6RemoteAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ie.NewTFTPFComponentIPv6LocalAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ), ie.NewTFTPacketFilter( ie.TFTPFDownlinkOnly, 2, 0, ie.NewTFTPFComponentProtocolIdentifierNextHeader(1), ie.NewTFTPFComponentSingleLocalPort(2152), ie.NewTFTPFComponentSingleRemotePort(2123), ie.NewTFTPFComponentLocalPortRange(2123, 2152), ie.NewTFTPFComponentRemotePortRange(2152, 2123), ), ie.NewTFTPacketFilter( ie.TFTPFUplinkOnly, 3, 0, ie.NewTFTPFComponentSecurityParameterIndex(0xdeadbeef), ie.NewTFTPFComponentTypeOfServiceTrafficClass(1, 2), ie.NewTFTPFComponentFlowLabel(0x00011111), ie.NewTFTPFComponentDestinationMACAddress(mac1), ie.NewTFTPFComponentSourceMACAddress(mac2), ), ie.NewTFTPacketFilter( ie.TFTPFBidirectional, 4, 0, ie.NewTFTPFComponentDot1QCTAGVID(0x0111), ie.NewTFTPFComponentDot1QSTAGVID(0x0222), ie.NewTFTPFComponentDot1QCTAGPCPDEI(3), ie.NewTFTPFComponentDot1QSTAGPCPDEI(5), ie.NewTFTPFComponentEthertype(0x0800), ), }, nil, []*ie.TFTParameter{ ie.NewTFTParameter(ie.TFTParamIDAuthorizationToken, []byte{0xde, 0xad, 0xbe, 0xef}), ie.NewTFTParameter(ie.TFTParamIDFlowIdentifier, []byte{0x11, 0x11, 0x22, 0x22}), ie.NewTFTParameter(ie.TFTParamIDPacketFileterIdentifier, []byte{0x01, 0x02, 0x03, 0x04}), }, }, { "TFTOpDeleteExistingTFT/NoParams", []byte{0x54, 0x00, 0x01, 0x00, 0x40}, nil, nil, nil, }, { "TFTOpDeleteExistingTFT/WithParams", []byte{ 0x54, 0x00, 0x13, 0x00, 0x50, 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x02, 0x04, 0x11, 0x11, 0x22, 0x22, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, }, nil, nil, []*ie.TFTParameter{ ie.NewTFTParameter(ie.TFTParamIDAuthorizationToken, []byte{0xde, 0xad, 0xbe, 0xef}), ie.NewTFTParameter(ie.TFTParamIDFlowIdentifier, []byte{0x11, 0x11, 0x22, 0x22}), ie.NewTFTParameter(ie.TFTParamIDPacketFileterIdentifier, []byte{0x01, 0x02, 0x03, 0x04}), }, }, { "TFTOpDeletePacketFilters/NoParams", []byte{0x54, 0x00, 0x05, 0x00, 0xa4, 0x01, 0x02, 0x03, 0x04}, nil, []uint8{1, 2, 3, 4}, nil, }, { "TFTOpDeletePacketFilters/WithParams", []byte{ 0x54, 0x00, 0x17, 0x00, 0xb4, 0x01, 0x02, 0x03, 0x04, 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x02, 0x04, 0x11, 0x11, 0x22, 0x22, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, }, nil, []uint8{1, 2, 3, 4}, []*ie.TFTParameter{ ie.NewTFTParameter(ie.TFTParamIDAuthorizationToken, []byte{0xde, 0xad, 0xbe, 0xef}), ie.NewTFTParameter(ie.TFTParamIDFlowIdentifier, []byte{0x11, 0x11, 0x22, 0x22}), ie.NewTFTParameter(ie.TFTParamIDPacketFileterIdentifier, []byte{0x01, 0x02, 0x03, 0x04}), }, }, } for _, c := range cases { t.Run(c.description, func(t *testing.T) { i, err := ie.Parse(c.serialized) if err != nil { t.Fatal(err) } f, err := i.TrafficFlowTemplate() if err != nil { t.Fatal(err) } if diff := cmp.Diff(f.PacketFilters, c.filters); diff != "" { t.Error(diff) } if diff := cmp.Diff(f.PacketFilterIdentifiers, c.ids); diff != "" { t.Error(diff) } if diff := cmp.Diff(f.Parameters, c.params); diff != "" { t.Error(diff) } }) } } ================================================ FILE: gtpv2/ie/throttling.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "time" ) // NewThrottling creates a new Timer IE. func NewThrottling(delay time.Duration, factor uint8) *IE { v := NewThrottlingFields(delay, factor) b, err := v.Marshal() if err != nil { return nil } return New(Throttling, 0x00, b) } // Throttling returns Throttling in time.Duration if the type of IE matches. func (i *IE) Throttling() (*ThrottlingFields, error) { switch i.Type { case Throttling: return ParseThrottlingFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // ThrottlingFields is a set of fields in Throttling IE. type ThrottlingFields struct { DelayUnit uint8 DelayValue time.Duration Factor uint8 } // NewThrottlingFields creates a new ThrottlingFields. func NewThrottlingFields(delay time.Duration, factor uint8) *ThrottlingFields { // 8.85 Throttling // Bits 5 to 1 represent the binary coded timer value. // Bits 6 to 8 defines the timer value unit as follows: // Bits // 8 7 6 // 0 0 0 value is incremented in multiples of 2 seconds // 0 0 1 value is incremented in multiples of 1 minute // 0 1 0 value is incremented in multiples of 10 minutes // 0 1 1 value is incremented in multiples of 1 hour // 1 0 0 value is incremented in multiples of 10 hours // 1 1 1 value indicates that the timer is infinite // // Other values shall be interpreted as multiples of 1 minute in this version of the protocol. // Timer unit and Timer value both set to all "zeros" shall be interpreted as an indication that the timer is stopped. var unit uint8 switch { case delay%(10*time.Hour) == 0: unit = 0x04 case delay%(1*time.Hour) == 0: unit = 0x03 case delay%(10*time.Minute) == 0: unit = 0x02 case delay%(1*time.Minute) == 0: unit = 0x01 case delay%(2*time.Second) == 0: unit = 0x00 default: unit = 0xe0 } return &ThrottlingFields{ DelayUnit: unit, DelayValue: delay, Factor: factor, } } // Marshal serializes ThrottlingFields. func (f *ThrottlingFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ThrottlingFields. func (f *ThrottlingFields) MarshalTo(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } var value uint8 switch f.DelayUnit { case 0x04: value = uint8(f.DelayValue / (10 * time.Hour)) case 0x03: value = uint8(f.DelayValue / time.Hour) case 0x02: value = uint8(f.DelayValue / (10 * time.Minute)) case 0x01: value = uint8(f.DelayValue / time.Minute) case 0x00: value = uint8(f.DelayValue / (2 * time.Second)) default: value = 0 } b[0] = ((f.DelayUnit << 5) & 0xe0) | (value & 0x1f) b[1] = f.Factor return nil } // ParseThrottlingFields decodes ThrottlingFields. func ParseThrottlingFields(b []byte) (*ThrottlingFields, error) { f := &ThrottlingFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into ThrottlingFields. func (f *ThrottlingFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 2 { return io.ErrUnexpectedEOF } f.DelayUnit = (b[0] & 0xe0) >> 5 switch f.DelayUnit { case 0x04: f.DelayValue = time.Duration(b[0]&0x1f) * (10 * time.Hour) case 0x03: f.DelayValue = time.Duration(b[0]&0x1f) * time.Hour case 0x02: f.DelayValue = time.Duration(b[0]&0x1f) * (10 * time.Minute) case 0x01: f.DelayValue = time.Duration(b[0]&0x1f) * time.Minute case 0x00: f.DelayValue = time.Duration(b[0]&0x1f) * (2 * time.Second) default: f.DelayValue = 0 } f.Factor = b[1] return nil } // MarshalLen returns the serial length of ThrottlingFields in int. func (f *ThrottlingFields) MarshalLen() int { return 2 } ================================================ FILE: gtpv2/ie/tmsi.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie // NewTMSI creates a new TMSI IE. func NewTMSI(tmsi uint32) *IE { return NewUint32IE(TMSI, tmsi) } // TMSI returns TMSI in uint32 if the type of IE matches. func (i *IE) TMSI() (uint32, error) { switch i.Type { case TMSI: return i.ValueAsUint32() default: return 0, &InvalidTypeError{Type: i.Type} } } // MustTMSI returns TMSI in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustTMSI() uint32 { v, _ := i.TMSI() return v } ================================================ FILE: gtpv2/ie/trace-reference.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "github.com/wmnsk/go-gtp/utils" ) // NewTraceReference creates a new TraceReference IE. func NewTraceReference(mcc, mnc string, traceID uint32) *IE { v := NewTraceReferenceFields(mcc, mnc, traceID) b, err := v.Marshal() if err != nil { return nil } return New(TraceReference, 0x00, b) } // TraceReferenceFields is a set of fields in TraceReference IE. type TraceReferenceFields struct { MCC, MNC string TraceID uint32 // 24-bit } // NewTraceReferenceFields creates a new TraceReferenceFields. func NewTraceReferenceFields(mcc, mnc string, traceID uint32) *TraceReferenceFields { return &TraceReferenceFields{ MCC: mcc, MNC: mnc, TraceID: traceID, } } // Marshal serializes TraceReferenceFields. func (f *TraceReferenceFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes TraceReferenceFields. func (f *TraceReferenceFields) MarshalTo(b []byte) error { l := len(b) if l < 3 { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.MCC, f.MNC) if err != nil { return err } copy(b[0:3], plmn) if l < 6 { return io.ErrUnexpectedEOF } copy(b[3:6], utils.Uint32To24(f.TraceID)) return nil } // ParseTraceReferenceFields decodes TraceReferenceFields. func ParseTraceReferenceFields(b []byte) (*TraceReferenceFields, error) { f := &TraceReferenceFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into TraceReferenceFields. func (f *TraceReferenceFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 3 { return io.ErrUnexpectedEOF } var err error f.MCC, f.MNC, err = utils.DecodePLMN(b[0:3]) if err != nil { return err } if l < 6 { return io.ErrUnexpectedEOF } f.TraceID = utils.Uint24To32(b[3:6]) return nil } // MarshalLen returns the serial length of TraceReferenceFields in int. func (f *TraceReferenceFields) MarshalLen() int { return 6 } // TraceID returns TraceID in uint32 if the type of IE matches. func (i *IE) TraceID() (uint32, error) { switch i.Type { case TraceReference, TraceInformation: if len(i.Payload) < 6 { return 0, io.ErrUnexpectedEOF } return utils.Uint24To32(i.Payload[3:6]), nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustTraceID returns TraceID in uint32, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustTraceID() uint32 { v, _ := i.TraceID() return v } ================================================ FILE: gtpv2/ie/uci.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "github.com/wmnsk/go-gtp/utils" ) // NewUserCSGInformation creates a new UserCSGInformation IE. func NewUserCSGInformation(mcc, mnc string, csgID uint32, mode, lcsg, cmi uint8) *IE { v := NewUserCSGInformationFields(mcc, mnc, csgID, mode, lcsg, cmi) b, err := v.Marshal() if err != nil { return nil } return New(UserCSGInformation, 0x00, b) } // UserCSGInformation returns UserCSGInformation in UserCSGInformationFields type if the type of IE matches. func (i *IE) UserCSGInformation() (*UserCSGInformationFields, error) { switch i.Type { case UserCSGInformation: return ParseUserCSGInformationFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // UserCSGInformationFields is a set of fields in UserCSGInformation IE. type UserCSGInformationFields struct { MCC, MNC string CSGID uint32 // 27-bit AccessMode uint8 // 7-8th bit, in the same octet as Flags Flags uint8 // 1-2th bit, in the same octet as AccessMode } // NewUserCSGInformationFields creates a new UserCSGInformationFields. func NewUserCSGInformationFields(mcc, mnc string, csgID uint32, mode, lcsg, cmi uint8) *UserCSGInformationFields { return &UserCSGInformationFields{ MCC: mcc, MNC: mnc, CSGID: csgID, AccessMode: mode, Flags: ((lcsg << 1) & 0x02) | (cmi & 0x01), } } // Marshal serializes UserCSGInformationFields. func (f *UserCSGInformationFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes UserCSGInformationFields. func (f *UserCSGInformationFields) MarshalTo(b []byte) error { l := len(b) if l < 3 { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.MCC, f.MNC) if err != nil { return err } copy(b[0:3], plmn) if l < 8 { return io.ErrUnexpectedEOF } binary.BigEndian.PutUint32(b[3:7], f.CSGID&0x7ffffff) b[7] = ((f.AccessMode & 0x03) << 6) | f.Flags return nil } // ParseUserCSGInformationFields decodes UserCSGInformationFields. func ParseUserCSGInformationFields(b []byte) (*UserCSGInformationFields, error) { f := &UserCSGInformationFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into UserCSGInformationFields. func (f *UserCSGInformationFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 3 { return io.ErrUnexpectedEOF } var err error f.MCC, f.MNC, err = utils.DecodePLMN(b[0:3]) if err != nil { return err } if l <= 7 { return io.ErrUnexpectedEOF } f.CSGID = binary.BigEndian.Uint32(b[3:7]) & 0x7ffffff f.AccessMode = b[7] >> 6 f.Flags = b[7] & 0x03 return nil } // MarshalLen returns the serial length of UserCSGInformationFields in int. func (f *UserCSGInformationFields) MarshalLen() int { return 8 } // AccessMode returns AccessMode in uint8 if the type of IE matches. func (i *IE) AccessMode() (uint8, error) { switch i.Type { case UserCSGInformation: if len(i.Payload) < 8 { return 0, io.ErrUnexpectedEOF } return i.Payload[7] >> 6, nil default: return 0, &InvalidTypeError{Type: i.Type} } } // MustAccessMode returns AccessMode in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustAccessMode() uint8 { v, _ := i.AccessMode() return v } ================================================ FILE: gtpv2/ie/ue-timezone.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "io" "math" "time" ) // NewUETimeZone creates a new UETimeZone IE. func NewUETimeZone(tz time.Duration, daylightSaving uint8) *IE { i := New(UETimeZone, 0x00, make([]byte, 2)) min := tz.Minutes() / 15 absMin := int(math.Abs(min)) hex := byte(((absMin % 10) << 4) | (absMin / 10)) if min < 0 { hex |= 0x08 } i.Payload[0] = hex i.Payload[1] = daylightSaving & 0x03 return i } // TimeZone returns TimeZone in time.Duration if the type of IE matches. func (i *IE) TimeZone() (time.Duration, error) { if i.Type != UETimeZone { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 1 { return 0, io.ErrUnexpectedEOF } unsigned := i.Payload[0] & 0xf7 dec := int((unsigned >> 4) + (unsigned&0x0f)*10) if (i.Payload[0]&0x08)>>3 == 1 { dec *= -1 } return time.Duration(dec*15) * time.Minute, nil } // MustTimeZone returns TimeZone in time.Duration, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustTimeZone() time.Duration { v, _ := i.TimeZone() return v } // DaylightSaving returns DaylightSaving in uint8 if the type of IE matches. func (i *IE) DaylightSaving() (uint8, error) { if i.Type != UETimeZone { return 0, &InvalidTypeError{Type: i.Type} } if len(i.Payload) < 2 { return 0, io.ErrUnexpectedEOF } return i.Payload[1], nil } // MustDaylightSaving returns DaylightSaving in uint8, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustDaylightSaving() uint8 { v, _ := i.DaylightSaving() return v } ================================================ FILE: gtpv2/ie/uli-timestamp.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "time" ) // NewULITimestamp creates a new ULITimestamp IE. func NewULITimestamp(ts time.Time) *IE { u64sec := uint64(ts.Sub(time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC))) / 1000000000 return NewUint32IE(ULITimestamp, uint32(u64sec)) } // Timestamp returns Timestamp in time.Time if the type of IE matches. func (i *IE) Timestamp() (time.Time, error) { if len(i.Payload) < 4 { return time.Time{}, io.ErrUnexpectedEOF } switch i.Type { case ULITimestamp, TWANIdentifierTimestamp: return time.Unix(int64(binary.BigEndian.Uint32(i.Payload)-2208988800), 0), nil default: return time.Time{}, &InvalidTypeError{Type: i.Type} } } // MustTimestamp returns Timestamp in time.Time, ignoring errors. // This should only be used if it is assured to have the value. func (i *IE) MustTimestamp() time.Time { v, _ := i.Timestamp() return v } ================================================ FILE: gtpv2/ie/uli.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "encoding/binary" "io" "github.com/wmnsk/go-gtp/utils" ) const ( cgilen int = 7 sailen int = 7 railen int = 7 tailen int = 5 ecgilen int = 7 lailen int = 5 menbilen int = 6 emenbilen int = 6 ) const ( uliFlagCGI uint8 = 0x01 uliFlagSAI uint8 = 0x02 uliFlagRAI uint8 = 0x04 uliFlagTAI uint8 = 0x08 uliFlagECGI uint8 = 0x10 uliFlagLAI uint8 = 0x20 uliFlagMENBI uint8 = 0x40 uliFlagEMENBI uint8 = 0x80 ) type PLMN struct { MCC string MNC string } // MCCFromPLMN gets MCC from PLMN. func (plmn *PLMN) MCCFromPLMN() string { return plmn.MCC } // MNCFromPLMN gets MNC from PLMN. func (plmn *PLMN) MNCFromPLMN() string { return plmn.MNC } // CGI represents a CGI, which is defined to be used as a field of UserLocationInformation IE. type CGI struct { *PLMN LAC uint16 CI uint16 } // NewCGI creates a new CGI. func NewCGI(mcc, mnc string, lac, ci uint16) *CGI { return &CGI{ PLMN: &PLMN{MCC: mcc, MNC: mnc}, LAC: lac, CI: ci, } } // HasCGI checks if UserLocationInformationFields has CGI. func (f *UserLocationInformationFields) HasCGI() bool { return f.Flags&uliFlagCGI != 0 } // SAI represents a SAI, which is defined to be used as a field of UserLocationInformation IE. type SAI struct { *PLMN LAC uint16 SAC uint16 } // NewSAI creates a new SAI. func NewSAI(mcc, mnc string, lac, sac uint16) *SAI { return &SAI{ PLMN: &PLMN{MCC: mcc, MNC: mnc}, LAC: lac, SAC: sac, } } // HasSAI checks if UserLocationInformationFields has SAI. func (f *UserLocationInformationFields) HasSAI() bool { return f.Flags&uliFlagSAI != 0 } // RAI represents a RAI, which is defined to be used as a field of UserLocationInformation IE. type RAI struct { *PLMN LAC uint16 RAC uint16 } // NewRAI creates a new RAI. func NewRAI(mcc, mnc string, lac, rac uint16) *RAI { return &RAI{ PLMN: &PLMN{MCC: mcc, MNC: mnc}, LAC: lac, RAC: rac, } } // HasRAI checks if UserLocationInformationFields has RAI. func (f *UserLocationInformationFields) HasRAI() bool { return f.Flags&uliFlagRAI != 0 } // TAI represents a TAI, which is defined to be used as a field of UserLocationInformation IE. type TAI struct { *PLMN TAC uint16 } // NewTAI creates a new TAI. func NewTAI(mcc, mnc string, tac uint16) *TAI { return &TAI{ PLMN: &PLMN{MCC: mcc, MNC: mnc}, TAC: tac, } } // HasTAI checks if UserLocationInformationFields has TAI. func (f *UserLocationInformationFields) HasTAI() bool { return f.Flags&uliFlagTAI != 0 } // ECGI represents a ECGI, which is defined to be used as a field of UserLocationInformation IE. type ECGI struct { *PLMN ECI uint32 } // NewECGI creates a new ECGI. func NewECGI(mcc, mnc string, eci uint32) *ECGI { return &ECGI{ PLMN: &PLMN{MCC: mcc, MNC: mnc}, ECI: eci & 0xfffffff, } } // HasECGI checks if UserLocationInformationFields has ECGI. func (f *UserLocationInformationFields) HasECGI() bool { return f.Flags&uliFlagECGI != 0 } // LAI represents a LAI, which is defined to be used as a field of UserLocationInformation IE. type LAI struct { *PLMN LAC uint16 } // NewLAI creates a new LAI. func NewLAI(mcc, mnc string, lac uint16) *LAI { return &LAI{ PLMN: &PLMN{MCC: mcc, MNC: mnc}, LAC: lac, } } // HasLAI checks if UserLocationInformationFields has LAI. func (f *UserLocationInformationFields) HasLAI() bool { return f.Flags&uliFlagLAI != 0 } // MENBI represents a MENBI, which is defined to be used as a field of UserLocationInformation IE. type MENBI struct { *PLMN MENBI uint32 } // NewMENBI creates a new MENBI. func NewMENBI(mcc, mnc string, menbi uint32) *MENBI { return &MENBI{ PLMN: &PLMN{MCC: mcc, MNC: mnc}, MENBI: menbi, } } // HasMENBI checks if UserLocationInformationFields has MENBI. func (f *UserLocationInformationFields) HasMENBI() bool { return f.Flags&uliFlagMENBI != 0 } // EMENBI represents a EMENBI, which is defined to be used as a field of UserLocationInformation IE. type EMENBI struct { *PLMN EMENBI uint32 } // NewEMENBI creates a new EMENBI. func NewEMENBI(mcc, mnc string, menbi uint32) *EMENBI { return &EMENBI{ PLMN: &PLMN{MCC: mcc, MNC: mnc}, EMENBI: menbi, } } // HasEMENBI checks if UserLocationInformationFields has EMENBI. func (f *UserLocationInformationFields) HasEMENBI() bool { return f.Flags&uliFlagEMENBI != 0 } // NewUserLocationInformationStruct creates a new UserLocationInformation IE from // the structs defined in gtpv2/ie package. Give nil for unnecessary values. func NewUserLocationInformationStruct(cgi *CGI, sai *SAI, rai *RAI, tai *TAI, ecgi *ECGI, lai *LAI, menbi *MENBI, emenbi *EMENBI) *IE { v := NewUserLocationInformationFields(cgi, sai, rai, tai, ecgi, lai, menbi, emenbi) b, err := v.Marshal() if err != nil { return nil } return New(UserLocationInformation, 0x00, b) } // UserLocationInformation returns UserLocationInformation in UserLocationInformationFields type // if the type of IE matches. func (i *IE) UserLocationInformation() (*UserLocationInformationFields, error) { switch i.Type { case UserLocationInformation: return ParseUserLocationInformationFields(i.Payload) default: return nil, &InvalidTypeError{Type: i.Type} } } // UserLocationInformationFields is a set of fields in UserLocationInformation IE. // // The contained fields are of type struct, as they are too complex to handle with // existing (standard) types in Go. type UserLocationInformationFields struct { Flags uint8 *CGI *SAI *RAI *TAI *ECGI *LAI *MENBI *EMENBI } // NewUserLocationInformationFields creates a new NewUserLocationInformationFields. func NewUserLocationInformationFields(cgi *CGI, sai *SAI, rai *RAI, tai *TAI, ecgi *ECGI, lai *LAI, menbi *MENBI, emenbi *EMENBI) *UserLocationInformationFields { f := &UserLocationInformationFields{} if cgi != nil { f.Flags |= uliFlagCGI f.CGI = cgi } if sai != nil { f.Flags |= uliFlagSAI f.SAI = sai } if rai != nil { f.Flags |= uliFlagRAI f.RAI = rai } if tai != nil { f.Flags |= uliFlagTAI f.TAI = tai } if ecgi != nil { f.Flags |= uliFlagECGI f.ECGI = ecgi } if lai != nil { f.Flags |= uliFlagLAI f.LAI = lai } if menbi != nil { f.Flags |= uliFlagMENBI f.MENBI = menbi } if emenbi != nil { f.Flags |= uliFlagEMENBI f.EMENBI = emenbi } return f } // Marshal serializes UserLocationInformationFields. func (f *UserLocationInformationFields) Marshal() ([]byte, error) { b := make([]byte, f.MarshalLen()) if err := f.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes UserLocationInformationFields. func (f *UserLocationInformationFields) MarshalTo(b []byte) error { l := len(b) if l < 1 { return io.ErrUnexpectedEOF } b[0] = f.Flags offset := 1 if has1stBit(f.Flags) { // CGI if l < offset+cgilen { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.CGI.MCC, f.CGI.MNC) if err != nil { return err } copy(b[offset:offset+3], plmn) binary.BigEndian.PutUint16(b[offset+3:offset+5], f.CGI.LAC) binary.BigEndian.PutUint16(b[offset+5:offset+7], f.CGI.CI) offset += cgilen } if has2ndBit(f.Flags) { // SAI if l < offset+sailen { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.SAI.MCC, f.SAI.MNC) if err != nil { return err } copy(b[offset:offset+3], plmn) binary.BigEndian.PutUint16(b[offset+3:offset+5], f.SAI.LAC) binary.BigEndian.PutUint16(b[offset+5:offset+7], f.SAI.SAC) offset += sailen } if has3rdBit(f.Flags) { // RAI if l < offset+railen { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.RAI.MCC, f.RAI.MNC) if err != nil { return err } copy(b[offset:offset+3], plmn) binary.BigEndian.PutUint16(b[offset+3:offset+5], f.RAI.LAC) binary.BigEndian.PutUint16(b[offset+5:offset+7], f.RAI.RAC) offset += railen } if has4thBit(f.Flags) { // TAI if l < offset+tailen { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.TAI.MCC, f.TAI.MNC) if err != nil { return err } copy(b[offset:offset+3], plmn) binary.BigEndian.PutUint16(b[offset+3:offset+5], f.TAI.TAC) offset += tailen } if has5thBit(f.Flags) { // ECGI if l < offset+ecgilen { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.ECGI.MCC, f.ECGI.MNC) if err != nil { return err } copy(b[offset:offset+3], plmn) binary.BigEndian.PutUint32(b[offset+3:offset+7], f.ECGI.ECI) offset += ecgilen } if has6thBit(f.Flags) { // LAI if l < offset+lailen { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.LAI.MCC, f.LAI.MNC) if err != nil { return err } copy(b[offset:offset+3], plmn) binary.BigEndian.PutUint16(b[offset+3:offset+5], f.LAI.LAC) offset += lailen } if has7thBit(f.Flags) { // MENBI if l < offset+menbilen { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.MENBI.MCC, f.MENBI.MNC) if err != nil { return err } copy(b[offset:offset+3], plmn) copy(b[offset+3:offset+6], utils.Uint32To24(f.MENBI.MENBI)) offset += menbilen } if has8thBit(f.Flags) { // EMENBI if l < offset+emenbilen { return io.ErrUnexpectedEOF } plmn, err := utils.EncodePLMN(f.EMENBI.MCC, f.EMENBI.MNC) if err != nil { return err } copy(b[offset:offset+3], plmn) copy(b[offset+3:offset+6], utils.Uint32To24(f.EMENBI.EMENBI)) } return nil } // ParseUserLocationInformationFields decodes UserLocationInformationFields. func ParseUserLocationInformationFields(b []byte) (*UserLocationInformationFields, error) { f := &UserLocationInformationFields{} if err := f.UnmarshalBinary(b); err != nil { return nil, err } return f, nil } // UnmarshalBinary decodes given bytes into UserLocationInformationFields. func (f *UserLocationInformationFields) UnmarshalBinary(b []byte) error { l := len(b) if l < 1 { return io.ErrUnexpectedEOF } f.Flags = b[0] offset := 1 var err error if has1stBit(f.Flags) { // CGI if l < offset+cgilen { return io.ErrUnexpectedEOF } f.CGI = &CGI{PLMN: &PLMN{}} f.CGI.PLMN.MCC, f.CGI.PLMN.MNC, err = utils.DecodePLMN(b[offset : offset+3]) if err != nil { return err } f.CGI.LAC = binary.BigEndian.Uint16(b[offset+3 : offset+5]) f.CGI.CI = binary.BigEndian.Uint16(b[offset+5 : offset+7]) offset += cgilen } if has2ndBit(f.Flags) { // SAI if l < offset+sailen { return io.ErrUnexpectedEOF } f.SAI = &SAI{PLMN: &PLMN{}} f.SAI.PLMN.MCC, f.SAI.PLMN.MNC, err = utils.DecodePLMN(b[offset : offset+3]) if err != nil { return err } f.SAI.LAC = binary.BigEndian.Uint16(b[offset+3 : offset+5]) f.SAI.SAC = binary.BigEndian.Uint16(b[offset+5 : offset+7]) offset += sailen } if has3rdBit(f.Flags) { // RAI if l < offset+railen { return io.ErrUnexpectedEOF } f.RAI = &RAI{PLMN: &PLMN{}} f.RAI.PLMN.MCC, f.RAI.PLMN.MNC, err = utils.DecodePLMN(b[offset : offset+3]) if err != nil { return err } f.RAI.LAC = binary.BigEndian.Uint16(b[offset+3 : offset+5]) f.RAI.RAC = binary.BigEndian.Uint16(b[offset+5 : offset+7]) offset += railen } if has4thBit(f.Flags) { // TAI if l < offset+tailen { return io.ErrUnexpectedEOF } f.TAI = &TAI{PLMN: &PLMN{}} f.TAI.PLMN.MCC, f.TAI.PLMN.MNC, err = utils.DecodePLMN(b[offset : offset+3]) if err != nil { return err } f.TAI.TAC = binary.BigEndian.Uint16(b[offset+3 : offset+5]) offset += tailen } if has5thBit(f.Flags) { // ECGI if l < offset+ecgilen { return io.ErrUnexpectedEOF } f.ECGI = &ECGI{PLMN: &PLMN{}} f.ECGI.PLMN.MCC, f.ECGI.PLMN.MNC, err = utils.DecodePLMN(b[offset : offset+3]) if err != nil { return err } f.ECGI.ECI = binary.BigEndian.Uint32(b[offset+3 : offset+7]) offset += ecgilen } if has6thBit(f.Flags) { // LAI if l < offset+lailen { return io.ErrUnexpectedEOF } f.LAI = &LAI{PLMN: &PLMN{}} f.LAI.PLMN.MCC, f.LAI.PLMN.MNC, err = utils.DecodePLMN(b[offset : offset+3]) if err != nil { return err } f.LAI.LAC = binary.BigEndian.Uint16(b[offset+3 : offset+5]) offset += lailen } if has7thBit(f.Flags) { // MENBI if l < offset+menbilen { return io.ErrUnexpectedEOF } f.MENBI = &MENBI{PLMN: &PLMN{}} f.MENBI.PLMN.MCC, f.MENBI.PLMN.MNC, err = utils.DecodePLMN(b[offset : offset+3]) if err != nil { return err } f.MENBI.MENBI = utils.Uint24To32(b[offset+3 : offset+6]) offset += menbilen } if has8thBit(f.Flags) { // EMENBI if l < offset+emenbilen { return io.ErrUnexpectedEOF } f.EMENBI = &EMENBI{PLMN: &PLMN{}} f.EMENBI.PLMN.MCC, f.EMENBI.PLMN.MNC, err = utils.DecodePLMN(b[offset : offset+3]) if err != nil { return err } f.EMENBI.EMENBI = utils.Uint24To32(b[offset+3 : offset+6]) } return nil } // MarshalLen returns the serial length of UserLocationInformationFields in int. func (f *UserLocationInformationFields) MarshalLen() int { l := 1 if has1stBit(f.Flags) { l += cgilen } if has2ndBit(f.Flags) { l += sailen } if has3rdBit(f.Flags) { l += railen } if has4thBit(f.Flags) { l += tailen } if has5thBit(f.Flags) { l += ecgilen } if has6thBit(f.Flags) { l += lailen } if has7thBit(f.Flags) { l += menbilen } if has8thBit(f.Flags) { l += emenbilen } return l } // NewUserLocationInformationLazy creates a new UserLocationInformation IE. // // The flags and corresponding fields are automatically set depending on the values given in int. // If a value is less than 0, the field is considered as missing. // // Deprecated: use NewUserLocationInformationStruct instead. At some point this will be removed. func NewUserLocationInformationLazy(mcc, mnc string, lac, ci, sac, rac, tac, eci, menbi, emenbi int) *IE { var hasCGI, hasSAI, hasRAI, hasTAI, hasECGI, hasLAI, hasMENBI, hasEMENBI uint8 if ci >= 0 { hasCGI = 1 } if sac >= 0 { hasSAI = 1 } if rac >= 0 { hasRAI = 1 } if tac >= 0 { hasTAI = 1 } if eci >= 0 { hasECGI = 1 } if lac >= 0 { hasLAI = 1 } if menbi >= 0 { hasMENBI = 1 } if emenbi >= 0 { hasEMENBI = 1 } return NewUserLocationInformation( hasCGI, hasSAI, hasRAI, hasTAI, hasECGI, hasLAI, hasMENBI, hasEMENBI, mcc, mnc, uint16(lac), uint16(ci), uint16(sac), uint16(rac), uint16(tac), uint32(eci), uint32(menbi), uint32(emenbi), ) } // NewUserLocationInformation creates a new UserLocationInformation IE. // // Deprecated: use NewUserLocationInformationStruct instead. At some point this will be removed. func NewUserLocationInformation( hasCGI, hasSAI, hasRAI, hasTAI, hasECGI, hasLAI, hasMENBI, hasEMENBI uint8, mcc, mnc string, lac, ci, sac, rac, tac uint16, eci, menbi, emenbi uint32, ) *IE { flags := ((hasEMENBI & 0x01) << 7) | ((hasMENBI & 0x01) << 6) | ((hasLAI & 0x01) << 5) | ((hasECGI & 0x01) << 4) | ((hasTAI & 0x01) << 3) | ((hasRAI & 0x01) << 2) | ((hasSAI & 0x01) << 1) | (hasCGI & 0x01) i := New(UserLocationInformation, 0x00, make([]byte, uliPayloadLen(flags))) i.Payload[0] = flags plmn, err := utils.EncodePLMN(mcc, mnc) if err != nil { return nil } offset := 1 if flags&0x01 == 1 { copy(i.Payload[offset:offset+3], plmn) binary.BigEndian.PutUint16(i.Payload[offset+3:offset+5], lac) binary.BigEndian.PutUint16(i.Payload[offset+5:offset+7], ci) offset += cgilen } if flags>>1&0x01 == 1 { copy(i.Payload[offset:offset+3], plmn) binary.BigEndian.PutUint16(i.Payload[offset+3:offset+5], lac) binary.BigEndian.PutUint16(i.Payload[offset+5:offset+7], sac) offset += sailen } if flags>>2&0x01 == 1 { copy(i.Payload[offset:offset+3], plmn) binary.BigEndian.PutUint16(i.Payload[offset+3:offset+5], lac) binary.BigEndian.PutUint16(i.Payload[offset+5:offset+7], rac) offset += railen } if flags>>3&0x01 == 1 { copy(i.Payload[offset:offset+3], plmn) binary.BigEndian.PutUint16(i.Payload[offset+3:offset+5], tac) offset += tailen } if flags>>4&0x01 == 1 { copy(i.Payload[offset:offset+3], plmn) eci &= 0xfffff binary.BigEndian.PutUint32(i.Payload[offset+3:offset+7], eci) offset += ecgilen } if flags>>5&0x01 == 1 { copy(i.Payload[offset:offset+3], plmn) binary.BigEndian.PutUint16(i.Payload[offset+3:offset+5], lac) offset += lailen } if flags>>6&0x01 == 1 { copy(i.Payload[offset:offset+3], plmn) copy(i.Payload[offset+3:offset+6], utils.Uint32To24(menbi)) offset += menbilen } if flags>>7&0x01 == 1 { copy(i.Payload[offset:offset+3], plmn) copy(i.Payload[offset+3:offset+6], utils.Uint32To24(emenbi)) } return i } func uliPayloadLen(flags uint8) int { l := 1 if flags&uliFlagCGI != 0 { l += cgilen } if flags&uliFlagSAI != 0 { l += sailen } if flags&uliFlagRAI != 0 { l += railen } if flags&uliFlagTAI != 0 { l += tailen } if flags&uliFlagECGI != 0 { l += ecgilen } if flags&uliFlagLAI != 0 { l += lailen } if flags&uliFlagMENBI != 0 { l += menbilen } if flags&uliFlagEMENBI != 0 { l += emenbilen } return l } // UserLocationInfo is a getter function to parse UserLocationInformationFields // // Deprecated: use UserLocationInformation instead. At some point this will be removed. func (i *IE) UserLocationInfo() (*UserLocationInformationFields, error) { var uli UserLocationInformationFields var plmn PLMN l := len(i.Payload) if l == 0 { return &uli, io.ErrUnexpectedEOF } offset := 1 if i.Payload[0]&0x01 == 1 { if l < (offset + cgilen) { return &uli, io.ErrUnexpectedEOF } var cgi CGI uli.CGI = &cgi uli.CGI.PLMN = &plmn uli.CGI.PLMN.MCC, uli.CGI.PLMN.MNC, _ = utils.DecodePLMN(i.Payload[offset : offset+3]) uli.CGI.LAC = binary.BigEndian.Uint16(i.Payload[offset+3 : offset+5]) uli.CGI.CI = binary.BigEndian.Uint16(i.Payload[offset+5 : offset+7]) offset += cgilen } if i.Payload[0]>>1&0x01 == 1 { if l < (offset + sailen) { return &uli, io.ErrUnexpectedEOF } var sai SAI uli.SAI = &sai uli.SAI.PLMN = &plmn uli.SAI.PLMN.MCC, uli.SAI.PLMN.MNC, _ = utils.DecodePLMN(i.Payload[offset : offset+3]) uli.SAI.LAC = binary.BigEndian.Uint16(i.Payload[offset+3 : offset+5]) uli.SAI.SAC = binary.BigEndian.Uint16(i.Payload[offset+5 : offset+7]) offset += sailen } if i.Payload[0]>>2&0x01 == 1 { if l < (offset + railen) { return &uli, io.ErrUnexpectedEOF } var rai RAI uli.RAI = &rai uli.RAI.PLMN = &plmn uli.RAI.PLMN.MCC, uli.RAI.PLMN.MNC, _ = utils.DecodePLMN(i.Payload[offset : offset+3]) uli.RAI.LAC = binary.BigEndian.Uint16(i.Payload[offset+3 : offset+5]) uli.RAI.RAC = binary.BigEndian.Uint16(i.Payload[offset+5 : offset+7]) offset += railen } if i.Payload[0]>>3&0x01 == 1 { if l < (offset + tailen) { return &uli, io.ErrUnexpectedEOF } var tai TAI uli.TAI = &tai uli.TAI.PLMN = &plmn uli.TAI.PLMN.MCC, uli.TAI.PLMN.MNC, _ = utils.DecodePLMN(i.Payload[offset : offset+3]) uli.TAI.TAC = binary.BigEndian.Uint16(i.Payload[offset+3 : offset+5]) offset += tailen } if i.Payload[0]>>4&0x01 == 1 { if l < (offset + ecgilen) { return &uli, io.ErrUnexpectedEOF } var ecgi ECGI uli.ECGI = &ecgi uli.ECGI.PLMN = &plmn uli.ECGI.PLMN.MCC, uli.ECGI.PLMN.MNC, _ = utils.DecodePLMN(i.Payload[offset : offset+3]) uli.ECGI.ECI = binary.BigEndian.Uint32(i.Payload[offset+3 : offset+7]) offset += ecgilen } if i.Payload[0]>>5&0x01 == 1 { if l < (offset + lailen) { return &uli, io.ErrUnexpectedEOF } var lai LAI uli.LAI = &lai uli.LAI.PLMN = &plmn uli.LAI.PLMN.MCC, uli.LAI.PLMN.MNC, _ = utils.DecodePLMN(i.Payload[offset : offset+3]) uli.LAI.LAC = binary.BigEndian.Uint16(i.Payload[offset+3 : offset+5]) offset += lailen } if i.Payload[0]>>6&0x01 == 1 { if l < (offset + menbilen) { return &uli, io.ErrUnexpectedEOF } var menbi MENBI uli.MENBI = &menbi uli.MENBI.PLMN = &plmn uli.MENBI.PLMN.MCC, uli.MENBI.PLMN.MNC, _ = utils.DecodePLMN(i.Payload[offset : offset+3]) uli.MENBI.MENBI = utils.Uint24To32(i.Payload[offset+3 : offset+6]) offset += menbilen } if i.Payload[0]>>7&0x01 == 1 { if l < (offset + emenbilen) { return &uli, io.ErrUnexpectedEOF } var emenbi EMENBI uli.EMENBI = &emenbi uli.EMENBI.PLMN = &plmn uli.EMENBI.PLMN.MCC, uli.EMENBI.PLMN.MNC, _ = utils.DecodePLMN(i.Payload[offset : offset+3]) uli.EMENBI.EMENBI = utils.Uint24To32(i.Payload[offset+3 : offset+6]) } return &uli, nil } ================================================ FILE: gtpv2/ie/uli_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie import ( "testing" ) func TestNewUserLocationInformationStruct(t *testing.T) { t.Run("Test UserLocationInformation unmarshal", func(t *testing.T) { uli := NewUserLocationInformationStruct( NewCGI("123", "45", 0x1111, 0x2222), NewSAI("123", "45", 0x1111, 0x3333), NewRAI("123", "45", 0x1111, 0x4444), NewTAI("123", "45", 0x5555), NewECGI("123", "45", 0x66666666), NewLAI("123", "45", 0x1111), NewMENBI("123", "45", 0x11111111), NewEMENBI("123", "45", 0x22222222), ) if uli == nil { t.Fatalf("Error in NewUserLocationInformationStruct") } uliFields, err := uli.UserLocationInformation() if err != nil { t.Fatalf("Error in unmarshal: %v", err) } // CGI if uliFields.CGI.LAC != 0x1111 { t.Errorf("wrong uliFields.CGI.LAC, got: 0x%x", uliFields.CGI.LAC) } if uliFields.CGI.CI != 0x2222 { t.Errorf("wrong uliFields.CGI.CI, got: 0x%x", uliFields.CGI.CI) } if uliFields.CGI.PLMN.MCC != "123" { t.Errorf("wrong uliFields.CGI.PLMN.MCC, got: %v", uliFields.CGI.PLMN.MCC) } if uliFields.CGI.PLMN.MNC != "45" { t.Errorf("wrong uliFields.CGI.PLMN.MNC, got: %v", uliFields.CGI.PLMN.MNC) } // SAI if uliFields.SAI.LAC != 0x1111 { t.Errorf("wrong uliFields.SAI.LAC, got: 0x%x", uliFields.SAI.LAC) } if uliFields.SAI.SAC != 0x3333 { t.Errorf("wrong uliFields.SAI.SAC, got: 0x%x", uliFields.SAI.SAC) } if uliFields.SAI.PLMN.MCC != "123" { t.Errorf("wrong uliFields.SAI.PLMN.MCC, got: %v", uliFields.SAI.PLMN.MCC) } if uliFields.SAI.PLMN.MNC != "45" { t.Errorf("wrong uliFields.SAI.PLMN.MNC, got: %v", uliFields.SAI.PLMN.MNC) } // RAI if uliFields.RAI.LAC != 0x1111 { t.Errorf("wrong uliFields.RAI.LAC, got: 0x%x", uliFields.RAI.LAC) } if uliFields.RAI.RAC != 0x4444 { t.Errorf("wrong uliFields.RAI.RAC, got: 0x%x", uliFields.RAI.RAC) } if uliFields.RAI.PLMN.MCC != "123" { t.Errorf("wrong uliFields.RAI.PLMN.MCC, got: %v", uliFields.RAI.PLMN.MCC) } if uliFields.RAI.PLMN.MNC != "45" { t.Errorf("wrong uliFields.RAI.PLMN.MNC, got: %v", uliFields.RAI.PLMN.MNC) } // TAI if uliFields.TAI.TAC != 0x5555 { t.Errorf("wrong uliFields.TAI.TAC, got: 0x%x", uliFields.TAI.TAC) } if uliFields.TAI.PLMN.MCC != "123" { t.Errorf("wrong uliFields.TAI.PLMN.MCC, got: %v", uliFields.TAI.PLMN.MCC) } if uliFields.TAI.PLMN.MNC != "45" { t.Errorf("wrong uliFields.TAI.PLMN.MNC, got: %v", uliFields.TAI.PLMN.MNC) } // ECGI if uliFields.ECGI.ECI != 0x6666666 { t.Errorf("wrong uliFields.ECGI.ECI, got: 0x%x", uliFields.ECGI.ECI) } if uliFields.ECGI.PLMN.MCC != "123" { t.Errorf("wrong uliFields.ECGI.PLMN.MCC, got: %v", uliFields.ECGI.PLMN.MCC) } if uliFields.ECGI.PLMN.MNC != "45" { t.Errorf("wrong uliFields.ECGI.PLMN.MNC, got: %v", uliFields.ECGI.PLMN.MNC) } // LAI if uliFields.LAI.LAC != 0x1111 { t.Errorf("wrong uliFields.LAI.LAC, got: 0x%x", uliFields.LAI.LAC) } if uliFields.LAI.PLMN.MCC != "123" { t.Errorf("wrong uliFields.LAI.PLMN.MCC, got: %v", uliFields.LAI.PLMN.MCC) } if uliFields.LAI.PLMN.MNC != "45" { t.Errorf("wrong uliFields.LAI.PLMN.MNC, got: %v", uliFields.LAI.PLMN.MNC) } // MENBI if uliFields.MENBI.MENBI != 0x111111 { t.Errorf("wrong uliFields.MENBI.MENBI, got: 0x%x", uliFields.MENBI.MENBI) } if uliFields.MENBI.PLMN.MCC != "123" { t.Errorf("wrong uliFields.MENBI.PLMN.MCC, got: %v", uliFields.MENBI.PLMN.MCC) } if uliFields.MENBI.PLMN.MNC != "45" { t.Errorf("wrong uliFields.MENBI.PLMN.MNC, got: %v", uliFields.MENBI.PLMN.MNC) } // EMENBI if uliFields.EMENBI.EMENBI != 0x222222 { t.Errorf("wrong uliFields.EMENBI.EMENBI, got: 0x%x", uliFields.EMENBI.EMENBI) } if uliFields.EMENBI.PLMN.MCC != "123" { t.Errorf("wrong uliFields.EMENBI.PLMN.MCC, got: %v", uliFields.EMENBI.PLMN.MCC) } if uliFields.EMENBI.PLMN.MNC != "45" { t.Errorf("wrong uliFields.EMENBI.PLMN.MNC, got: %v", uliFields.EMENBI.PLMN.MNC) } }) } ================================================ FILE: gtpv2/ie/utils.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ie func has8thBit(f uint8) bool { return (f&0x80)>>7 == 1 } func has7thBit(f uint8) bool { return (f&0x40)>>6 == 1 } func has6thBit(f uint8) bool { return (f&0x20)>>5 == 1 } func has5thBit(f uint8) bool { return (f&0x010)>>4 == 1 } func has4thBit(f uint8) bool { return (f&0x08)>>3 == 1 } func has3rdBit(f uint8) bool { return (f&0x04)>>2 == 1 } func has2ndBit(f uint8) bool { return (f&0x02)>>1 == 1 } func has1stBit(f uint8) bool { return (f & 0x01) == 1 } ================================================ FILE: gtpv2/logger.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv2 import ( "io" "log" "os" "sync" ) var ( logger = log.New(os.Stderr, "", log.LstdFlags) logMu sync.Mutex ) // SetLogger replaces the standard logger with arbitrary *log.Logger. // // This package prints just informational logs from goroutines working background // that might help developers test the program but can be ignored safely. More // important ones that needs any action by caller would be returned as errors. func SetLogger(l *log.Logger) { if l == nil { log.Println("Don't pass nil to SetLogger: use DisableLogging instead.") } setLogger(l) } // EnableLogging enables the logging from the package. // If l is nil, it uses default logger provided by the package. // Logging is enabled by default. // // See also: SetLogger. func EnableLogging(l *log.Logger) { logMu.Lock() defer logMu.Unlock() setLogger(l) } // DisableLogging disables the logging from the package. // Logging is enabled by default. func DisableLogging() { logMu.Lock() defer logMu.Unlock() logger.SetOutput(io.Discard) } func setLogger(l *log.Logger) { if l == nil { l = log.New(os.Stderr, "", log.LstdFlags) } logMu.Lock() defer logMu.Unlock() logger = l } func logf(format string, v ...interface{}) { logMu.Lock() defer logMu.Unlock() logger.Printf(format, v...) } ================================================ FILE: gtpv2/message/change-notification-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // ChangeNotificationRequest is a ChangeNotificationRequest Header and its IEs above. type ChangeNotificationRequest struct { *Header IMSI *ie.IE MEI *ie.IE IndicationFlags *ie.IE RATType *ie.IE ULI *ie.IE UCI *ie.IE PGWS5S8IPAddressForControlPlane *ie.IE LinkedEBI *ie.IE PresenceReportingAreaInformation []*ie.IE MOExceptionDataCounter *ie.IE SecondaryRATUsageDataReport []*ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewChangeNotificationRequest creates a new ChangeNotificationRequest. func NewChangeNotificationRequest(teid, seq uint32, ies ...*ie.IE) *ChangeNotificationRequest { c := &ChangeNotificationRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeChangeNotificationRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.MobileEquipmentIdentity: c.MEI = i case ie.Indication: c.IndicationFlags = i case ie.RATType: c.RATType = i case ie.UserLocationInformation: c.ULI = i case ie.UserCSGInformation: c.UCI = i case ie.IPAddress: c.PGWS5S8IPAddressForControlPlane = i case ie.EPSBearerID: c.LinkedEBI = i case ie.PresenceReportingAreaInformation: c.PresenceReportingAreaInformation = append(c.PresenceReportingAreaInformation, i) case ie.Counter: c.MOExceptionDataCounter = i case ie.SecondaryRATUsageDataReport: c.SecondaryRATUsageDataReport = append(c.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes ChangeNotificationRequest into bytes. func (c *ChangeNotificationRequest) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ChangeNotificationRequest into bytes. func (c *ChangeNotificationRequest) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.IMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MEI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RATType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ULI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UCI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWS5S8IPAddressForControlPlane; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.LinkedEBI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaInformation { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MOExceptionDataCounter; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.SecondaryRATUsageDataReport { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseChangeNotificationRequest decodes given bytes as ChangeNotificationRequest. func ParseChangeNotificationRequest(b []byte) (*ChangeNotificationRequest, error) { c := &ChangeNotificationRequest{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as ChangeNotificationRequest. func (c *ChangeNotificationRequest) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.MobileEquipmentIdentity: c.MEI = i case ie.Indication: c.IndicationFlags = i case ie.RATType: c.RATType = i case ie.UserLocationInformation: c.ULI = i case ie.UserCSGInformation: c.UCI = i case ie.IPAddress: c.PGWS5S8IPAddressForControlPlane = i case ie.EPSBearerID: c.LinkedEBI = i case ie.PresenceReportingAreaInformation: c.PresenceReportingAreaInformation = append(c.PresenceReportingAreaInformation, i) case ie.Counter: c.MOExceptionDataCounter = i case ie.SecondaryRATUsageDataReport: c.SecondaryRATUsageDataReport = append(c.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *ChangeNotificationRequest) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.IMSI; ie != nil { l += ie.MarshalLen() } if ie := c.MEI; ie != nil { l += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := c.RATType; ie != nil { l += ie.MarshalLen() } if ie := c.ULI; ie != nil { l += ie.MarshalLen() } if ie := c.UCI; ie != nil { l += ie.MarshalLen() } if ie := c.PGWS5S8IPAddressForControlPlane; ie != nil { l += ie.MarshalLen() } if ie := c.LinkedEBI; ie != nil { l += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaInformation { l += ie.MarshalLen() } if ie := c.MOExceptionDataCounter; ie != nil { l += ie.MarshalLen() } for _, ie := range c.SecondaryRATUsageDataReport { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *ChangeNotificationRequest) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *ChangeNotificationRequest) MessageTypeName() string { return "Change Notification Request" } // TEID returns the TEID in uint32. func (c *ChangeNotificationRequest) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/change-notification-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestChangeNotificationRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewChangeNotificationRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIMSI("123451234567890"), ie.NewMobileEquipmentIdentity("123450123456789"), ie.NewIndication( 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ), ie.NewRATType(gtpv2.RATTypeEUTRAN), ie.NewUserLocationInformationStruct( ie.NewCGI("123", "45", 0x1111, 0x2222), ie.NewSAI("123", "45", 0x1111, 0x3333), ie.NewRAI("123", "45", 0x1111, 0x4444), ie.NewTAI("123", "45", 0x5555), ie.NewECGI("123", "45", 0x66666666), ie.NewLAI("123", "45", 0x1111), ie.NewMENBI("123", "45", 0x11111111), ie.NewEMENBI("123", "45", 0x22222222), ), ie.NewUserCSGInformation("123", "45", 0x00ffffff, gtpv2.AccessModeHybrid, 0, gtpv2.CMICSG), ie.NewIPAddress("1.1.1.1"), ie.NewEPSBearerID(0x05), ie.NewPrivateExtension(10415, []byte{0xde, 0xad, 0xbe, 0xef}), ), Serialized: []byte{ // Header 0x48, 0x26, 0x00, 0x8c, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, // MAI 0x4b, 0x00, 0x08, 0x00, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9, // IndicationFlags 0x4d, 0x00, 0x09, 0x00, 0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40, 0xa0, 0x01, // RATType 0x52, 0x00, 0x01, 0x00, 0x06, // ULI // -------------------------------------- 0x56, 0x00, 0x33, 0x00, // Flags 0xff, // CGI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x22, 0x22, // SAI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x33, 0x33, // RAI 0x21, 0xf3, 0x54, 0x11, 0x11, 0x44, 0x44, // TAI 0x21, 0xf3, 0x54, 0x55, 0x55, // ECGI 0x21, 0xf3, 0x54, 0x06, 0x66, 0x66, 0x66, // RAI 0x21, 0xf3, 0x54, 0x11, 0x11, // Macro eNB ID 0x21, 0xf3, 0x54, 0x11, 0x11, 0x11, // Extended Macro eNB ID 0x21, 0xf3, 0x54, 0x22, 0x22, 0x22, // -------------------------------------- // UCI 0x91, 0x00, 0x08, 0x00, 0x21, 0xf3, 0x54, 0x00, 0xff, 0xff, 0xff, 0x41, // PGWS5S8IPAddressForControlPlane 0x4a, 0x00, 0x04, 0x00, 0x01, 0x01, 0x01, 0x01, // LinkedEBI 0x49, 0x00, 0x01, 0x00, 0x05, // PrivateExtension 0xff, 0x00, 0x06, 0x00, 0x28, 0xaf, 0xde, 0xad, 0xbe, 0xef, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseChangeNotificationRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/change-notification-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // ChangeNotificationResponse is a ChangeNotificationResponse Header and its IEs above. type ChangeNotificationResponse struct { *Header IMSI *ie.IE MEI *ie.IE Cause *ie.IE ChangeReportingAction *ie.IE CSGInformationReportingAction *ie.IE PresenceReportingAreaAction []*ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewChangeNotificationResponse creates a new ChangeNotificationResponse. func NewChangeNotificationResponse(teid, seq uint32, ies ...*ie.IE) *ChangeNotificationResponse { c := &ChangeNotificationResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeChangeNotificationResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.MobileEquipmentIdentity: c.MEI = i case ie.Cause: c.Cause = i case ie.ChangeReportingAction: c.ChangeReportingAction = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.PresenceReportingAreaAction: c.PresenceReportingAreaAction = append(c.PresenceReportingAreaAction, i) case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes ChangeNotificationResponse into bytes. func (c *ChangeNotificationResponse) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ChangeNotificationResponse into bytes. func (c *ChangeNotificationResponse) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.IMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MEI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChangeReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaAction { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseChangeNotificationResponse decodes given bytes as ChangeNotificationResponse. func ParseChangeNotificationResponse(b []byte) (*ChangeNotificationResponse, error) { c := &ChangeNotificationResponse{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as ChangeNotificationResponse. func (c *ChangeNotificationResponse) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { switch i.Type { case ie.IMSI: c.IMSI = i case ie.MobileEquipmentIdentity: c.MEI = i case ie.Cause: c.Cause = i case ie.ChangeReportingAction: c.ChangeReportingAction = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.PresenceReportingAreaAction: c.PresenceReportingAreaAction = append(c.PresenceReportingAreaAction, i) case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *ChangeNotificationResponse) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.IMSI; ie != nil { l += ie.MarshalLen() } if ie := c.MEI; ie != nil { l += ie.MarshalLen() } if ie := c.Cause; ie != nil { l += ie.MarshalLen() } if ie := c.ChangeReportingAction; ie != nil { l += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { l += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaAction { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *ChangeNotificationResponse) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *ChangeNotificationResponse) MessageTypeName() string { return "Change Notification Response" } // TEID returns the TEID in uint32. func (c *ChangeNotificationResponse) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/change-notification-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestChangeNotificationResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewChangeNotificationResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIMSI("123451234567890"), ie.NewMobileEquipmentIdentity("123450123456789"), ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewPrivateExtension(10415, []byte{0xde, 0xad, 0xbe, 0xef}), ), Serialized: []byte{ // Header 0x48, 0x27, 0x00, 0x30, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, // MAI 0x4b, 0x00, 0x08, 0x00, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // PrivateExtension 0xff, 0x00, 0x06, 0x00, 0x28, 0xaf, 0xde, 0xad, 0xbe, 0xef, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseChangeNotificationResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/context-ack.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // ContextAcknowledge is a ContextAcknowledge Header and its IEs above. type ContextAcknowledge struct { *Header Cause *ie.IE IndicationFlags *ie.IE ForwardingFTEID *ie.IE BearerContexts []*ie.IE SGSNNumber *ie.IE MMENumberForMTSMS *ie.IE SGSNIdentifierForMTSMS *ie.IE MMEIdentifierForMTSMS *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewContextAcknowledge creates a new ContextAcknowledge. func NewContextAcknowledge(teid, seq uint32, ies ...*ie.IE) *ContextAcknowledge { c := &ContextAcknowledge{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeContextAcknowledge, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.Indication: c.IndicationFlags = i case ie.FullyQualifiedTEID: c.ForwardingFTEID = i case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.NodeNumber: switch i.Instance() { case 0: c.SGSNNumber = i case 1: c.MMENumberForMTSMS = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.NodeIdentifier: switch i.Instance() { case 0: c.SGSNIdentifierForMTSMS = i case 1: c.MMEIdentifierForMTSMS = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes ContextAcknowledge into bytes. func (c *ContextAcknowledge) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ContextAcknowledge into bytes. func (c *ContextAcknowledge) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ForwardingFTEID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.BearerContexts { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNNumber; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMENumberForMTSMS; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNIdentifierForMTSMS; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMEIdentifierForMTSMS; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseContextAcknowledge decodes given bytes as ContextAcknowledge. func ParseContextAcknowledge(b []byte) (*ContextAcknowledge, error) { c := &ContextAcknowledge{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as ContextAcknowledge. func (c *ContextAcknowledge) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.Indication: c.IndicationFlags = i case ie.FullyQualifiedTEID: c.ForwardingFTEID = i case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.NodeNumber: switch i.Instance() { case 0: c.SGSNNumber = i case 1: c.MMENumberForMTSMS = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.NodeIdentifier: switch i.Instance() { case 0: c.SGSNIdentifierForMTSMS = i case 1: c.MMEIdentifierForMTSMS = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *ContextAcknowledge) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.Cause; ie != nil { l += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := c.ForwardingFTEID; ie != nil { l += ie.MarshalLen() } for _, ie := range c.BearerContexts { l += ie.MarshalLen() } if ie := c.SGSNNumber; ie != nil { l += ie.MarshalLen() } if ie := c.MMENumberForMTSMS; ie != nil { l += ie.MarshalLen() } if ie := c.SGSNIdentifierForMTSMS; ie != nil { l += ie.MarshalLen() } if ie := c.MMEIdentifierForMTSMS; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *ContextAcknowledge) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *ContextAcknowledge) MessageTypeName() string { return "Context Acknowledge" } // TEID returns the TEID in uint32. func (c *ContextAcknowledge) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/context-ack_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ContextAcknowledge into bytes. // // Deprecated: use ContextAcknowledge.Marshal instead. func (c *ContextAcknowledge) Serialize() ([]byte, error) { log.Println("ContextAcknowledge.Serialize is deprecated. use ContextAcknowledge.Marshal instead") return c.Marshal() } // SerializeTo serializes ContextAcknowledge into bytes given as b. // // Deprecated: use ContextAcknowledge.MarshalTo instead. func (c *ContextAcknowledge) SerializeTo(b []byte) error { log.Println("ContextAcknowledge.SerializeTo is deprecated. use ContextAcknowledge.MarshalTo instead") return c.MarshalTo(b) } // DecodeContextAcknowledge decodes bytes as ContextAcknowledge. // // Deprecated: use ParseContextAcknowledge instead. func DecodeContextAcknowledge(b []byte) (*ContextAcknowledge, error) { log.Println("DecodeContextAcknowledge is deprecated. use ParseContextAcknowledge instead") return ParseContextAcknowledge(b) } // DecodeFromBytes decodes bytes as ContextAcknowledge. // // Deprecated: use ContextAcknowledge.UnmarshalBinary instead. func (c *ContextAcknowledge) DecodeFromBytes(b []byte) error { log.Println("ContextAcknowledge.DecodeFromBytes is deprecated. use ContextAcknowledge.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of ContextAcknowledge. // // Deprecated: use ContextAcknowledge.MarshalLen instead. func (c *ContextAcknowledge) Len() int { log.Println("ContextAcknowledge.Len is deprecated. use ContextAcknowledge.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv2/message/context-ack_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestContextAcknowledge(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewContextAcknowledge( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS4SGSNGTPU, 0xffffffff, "1.1.1.1", ""), ie.NewBearerContext( ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ie.NewBearerContext( ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ), Serialized: []byte{ // Header 0x48, 0x84, 0x00, 0x47, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // F-TEID 0x57, 0x00, 0x09, 0x00, 0x8f, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, // BearerContext 1 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, // BearerContext 2 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseContextAcknowledge(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/context-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // ContextRequest is a ContextRequest Header and its IEs above. type ContextRequest struct { *Header IMSI *ie.IE GUTI *ie.IE RAI *ie.IE PTMSI *ie.IE PTMSISignature *ie.IE CompleteTAURequestMessage *ie.IE AddressAndTEIDForCPlane *ie.IE UDPSourcePortNumber *ie.IE RATType *ie.IE Indication *ie.IE HopCounter *ie.IE TargetPLMNID *ie.IE MMESGSNLDN *ie.IE SGSNNodeName *ie.IE MMENodeName *ie.IE SGSNNumber *ie.IE SGSNIdentifier *ie.IE MMEIdentifier *ie.IE CIoTOptimizationsSupportIndication *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewContextRequest creates a new ContextRequest. func NewContextRequest(teid, seq uint32, ies ...*ie.IE) *ContextRequest { c := &ContextRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeContextRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.GUTI: c.GUTI = i case ie.UserLocationInformation: c.RAI = i case ie.PacketTMSI: c.PTMSI = i case ie.PTMSISignature: c.PTMSISignature = i case ie.CompleteRequestMessage: c.CompleteTAURequestMessage = i case ie.FullyQualifiedTEID: c.AddressAndTEIDForCPlane = i case ie.PortNumber: c.UDPSourcePortNumber = i case ie.RATType: c.RATType = i case ie.Indication: c.Indication = i case ie.HopCounter: c.HopCounter = i case ie.ServingNetwork: c.TargetPLMNID = i case ie.LocalDistinguishedName: c.MMESGSNLDN = i case ie.FullyQualifiedDomainName: switch i.Instance() { case 0: c.SGSNNodeName = i case 1: c.MMENodeName = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.NodeNumber: c.SGSNNumber = i case ie.NodeIdentifier: switch i.Instance() { case 0: c.SGSNIdentifier = i case 1: c.MMEIdentifier = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.CIoTOptimizationsSupportIndication: c.CIoTOptimizationsSupportIndication = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes ContextRequest into bytes. func (c *ContextRequest) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ContextRequest into bytes. func (c *ContextRequest) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.IMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.GUTI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RAI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PTMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PTMSISignature; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CompleteTAURequestMessage; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.AddressAndTEIDForCPlane; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UDPSourcePortNumber; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RATType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Indication; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.HopCounter; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TargetPLMNID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMESGSNLDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNNodeName; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMENodeName; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNNumber; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNIdentifier; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMEIdentifier; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CIoTOptimizationsSupportIndication; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseContextRequest decodes given bytes as ContextRequest. func ParseContextRequest(b []byte) (*ContextRequest, error) { c := &ContextRequest{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as ContextRequest. func (c *ContextRequest) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.GUTI: c.GUTI = i case ie.UserLocationInformation: c.RAI = i case ie.PacketTMSI: c.PTMSI = i case ie.PTMSISignature: c.PTMSISignature = i case ie.CompleteRequestMessage: c.CompleteTAURequestMessage = i case ie.FullyQualifiedTEID: c.AddressAndTEIDForCPlane = i case ie.PortNumber: c.UDPSourcePortNumber = i case ie.RATType: c.RATType = i case ie.Indication: c.Indication = i case ie.HopCounter: c.HopCounter = i case ie.ServingNetwork: c.TargetPLMNID = i case ie.LocalDistinguishedName: c.MMESGSNLDN = i case ie.FullyQualifiedDomainName: switch i.Instance() { case 0: c.SGSNNodeName = i case 1: c.MMENodeName = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.NodeNumber: c.SGSNNumber = i case ie.NodeIdentifier: switch i.Instance() { case 0: c.SGSNIdentifier = i case 1: c.MMEIdentifier = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.CIoTOptimizationsSupportIndication: c.CIoTOptimizationsSupportIndication = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *ContextRequest) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.IMSI; ie != nil { l += ie.MarshalLen() } if ie := c.GUTI; ie != nil { l += ie.MarshalLen() } if ie := c.RAI; ie != nil { l += ie.MarshalLen() } if ie := c.PTMSI; ie != nil { l += ie.MarshalLen() } if ie := c.PTMSISignature; ie != nil { l += ie.MarshalLen() } if ie := c.CompleteTAURequestMessage; ie != nil { l += ie.MarshalLen() } if ie := c.AddressAndTEIDForCPlane; ie != nil { l += ie.MarshalLen() } if ie := c.UDPSourcePortNumber; ie != nil { l += ie.MarshalLen() } if ie := c.RATType; ie != nil { l += ie.MarshalLen() } if ie := c.Indication; ie != nil { l += ie.MarshalLen() } if ie := c.HopCounter; ie != nil { l += ie.MarshalLen() } if ie := c.TargetPLMNID; ie != nil { l += ie.MarshalLen() } if ie := c.MMESGSNLDN; ie != nil { l += ie.MarshalLen() } if ie := c.SGSNNodeName; ie != nil { l += ie.MarshalLen() } if ie := c.MMENodeName; ie != nil { l += ie.MarshalLen() } if ie := c.SGSNNumber; ie != nil { l += ie.MarshalLen() } if ie := c.SGSNIdentifier; ie != nil { l += ie.MarshalLen() } if ie := c.MMEIdentifier; ie != nil { l += ie.MarshalLen() } if ie := c.CIoTOptimizationsSupportIndication; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *ContextRequest) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *ContextRequest) MessageTypeName() string { return "Context Request" } // TEID returns the TEID in uint32. func (c *ContextRequest) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/context-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ContextRequest into bytes. // // Deprecated: use ContextRequest.Marshal instead. func (c *ContextRequest) Serialize() ([]byte, error) { log.Println("ContextRequest.Serialize is deprecated. use ContextRequest.Marshal instead") return c.Marshal() } // SerializeTo serializes ContextRequest into bytes given as b. // // Deprecated: use ContextRequest.MarshalTo instead. func (c *ContextRequest) SerializeTo(b []byte) error { log.Println("ContextRequest.SerializeTo is deprecated. use ContextRequest.MarshalTo instead") return c.MarshalTo(b) } // DecodeContextRequest decodes bytes as ContextRequest. // // Deprecated: use ParseContextRequest instead. func DecodeContextRequest(b []byte) (*ContextRequest, error) { log.Println("DecodeContextRequest is deprecated. use ParseContextRequest instead") return ParseContextRequest(b) } // DecodeFromBytes decodes bytes as ContextRequest. // // Deprecated: use ContextRequest.UnmarshalBinary instead. func (c *ContextRequest) DecodeFromBytes(b []byte) error { log.Println("ContextRequest.DecodeFromBytes is deprecated. use ContextRequest.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of ContextRequest. // // Deprecated: use ContextRequest.MarshalLen instead. func (c *ContextRequest) Len() int { log.Println("ContextRequest.Len is deprecated. use ContextRequest.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv2/message/context-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestContextRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewContextRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIMSI("123451234567890"), ie.NewPacketTMSI(0xdeadbeef), ie.NewPTMSISignature(0xbeebee), ), Serialized: []byte{ // Header 0x48, 0x82, 0x00, 0x23, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, // P-TMSI 0x6f, 0x00, 0x04, 0x00, 0xde, 0xad, 0xbe, 0xef, // P-TMSI Signature 0x70, 0x00, 0x03, 0x00, 0xbe, 0xeb, 0xee, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseContextRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/context-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // ContextResponse is a ContextResponse Header and its IEs above. type ContextResponse struct { *Header Cause *ie.IE IMSI *ie.IE UEMMContext *ie.IE UEPDNConnections []*ie.IE SenderFTEID *ie.IE SGWS11S4FTEID *ie.IE SGWNodeName *ie.IE IndicationFlags *ie.IE TraceInformation *ie.IE S101IPAddress *ie.IE S102IPAddress *ie.IE SubscribedRFSPIndex *ie.IE RFSPIndexInUse *ie.IE UETimeZone *ie.IE MMESGSNLDN *ie.IE MDTConfiguration *ie.IE SGSNNodeName *ie.IE MMENodeName *ie.IE UCI *ie.IE MonitoringEventInformation *ie.IE MonitoringEventExtensionInformation *ie.IE UEUsageType *ie.IE SCEFPDNConnection []*ie.IE RATType *ie.IE ServingPLMNRateControl *ie.IE MOExceptionDataCounter *ie.IE RemainingRunningServiceGapTimer *ie.IE ExtendedTraceInformation *ie.IE SubscribedAdditionalRRMPolicyIndex *ie.IE AdditionalRRMPolicyIndexInUse *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewContextResponse creates a new ContextResponse. func NewContextResponse(teid, seq uint32, ies ...*ie.IE) *ContextResponse { c := &ContextResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeContextResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.IMSI: c.IMSI = i case ie.MMContextEPSSecurityContextQuadrupletsAndQuintuplets, ie.MMContextGSMKeyAndTriplets, ie.MMContextGSMKeyUsedCipherAndQuintuplets, ie.MMContextUMTSKeyAndQuintuplets, ie.MMContextUMTSKeyQuadrupletsAndQuintuplets, ie.MMContextUMTSKeyUsedCipherAndQuintuplets: if c.UEMMContext != nil { c.UEMMContext = i } else { c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PDNConnection: c.UEPDNConnections = append(c.UEPDNConnections, i) case ie.FullyQualifiedTEID: switch i.Instance() { case 0: c.SenderFTEID = i case 1: c.SGWS11S4FTEID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FullyQualifiedDomainName: switch i.Instance() { case 0: c.SGWNodeName = i case 1: c.SGSNNodeName = i case 2: c.MMENodeName = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.Indication: c.IndicationFlags = i case ie.TraceInformation: c.TraceInformation = i case ie.IPAddress: switch i.Instance() { case 0: c.S101IPAddress = i case 1: c.S102IPAddress = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.RFSPIndex: switch i.Instance() { case 0: c.SubscribedRFSPIndex = i case 1: c.RFSPIndexInUse = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.UETimeZone: c.UETimeZone = i case ie.LocalDistinguishedName: c.MMESGSNLDN = i case ie.MDTConfiguration: c.MDTConfiguration = i case ie.UserCSGInformation: c.UCI = i case ie.MonitoringEventInformation: c.MonitoringEventInformation = i case ie.MonitoringEventExtensionInformation: c.MonitoringEventExtensionInformation = i case ie.IntegerNumber: switch i.Instance() { case 0: c.UEUsageType = i case 1: c.RemainingRunningServiceGapTimer = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.SCEFPDNConnection: c.SCEFPDNConnection = append(c.SCEFPDNConnection, i) case ie.RATType: c.RATType = i case ie.ServingPLMNRateControl: c.ServingPLMNRateControl = i case ie.Counter: c.MOExceptionDataCounter = i case ie.ExtendedTraceInformation: c.ExtendedTraceInformation = i case ie.AdditionalRRMPolicyIndex: switch i.Instance() { case 0: c.SubscribedAdditionalRRMPolicyIndex = i case 1: c.AdditionalRRMPolicyIndexInUse = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes ContextResponse into bytes. func (c *ContextResponse) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ContextResponse into bytes. func (c *ContextResponse) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UEMMContext; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.UEPDNConnections { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SenderFTEID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWS11S4FTEID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWNodeName; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TraceInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.S101IPAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.S102IPAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SubscribedRFSPIndex; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RFSPIndexInUse; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UETimeZone; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMESGSNLDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MDTConfiguration; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGSNNodeName; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMENodeName; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UCI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MonitoringEventInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UEUsageType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.SCEFPDNConnection { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RATType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ServingPLMNRateControl; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MOExceptionDataCounter; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RemainingRunningServiceGapTimer; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ExtendedTraceInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseContextResponse decodes given bytes as ContextResponse. func ParseContextResponse(b []byte) (*ContextResponse, error) { c := &ContextResponse{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as ContextResponse. func (c *ContextResponse) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.IMSI: c.IMSI = i case ie.MMContextEPSSecurityContextQuadrupletsAndQuintuplets, ie.MMContextGSMKeyAndTriplets, ie.MMContextGSMKeyUsedCipherAndQuintuplets, ie.MMContextUMTSKeyAndQuintuplets, ie.MMContextUMTSKeyQuadrupletsAndQuintuplets, ie.MMContextUMTSKeyUsedCipherAndQuintuplets: if c.UEMMContext != nil { c.UEMMContext = i } else { c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PDNConnection: c.UEPDNConnections = append(c.UEPDNConnections, i) case ie.FullyQualifiedTEID: switch i.Instance() { case 0: c.SenderFTEID = i case 1: c.SGWS11S4FTEID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FullyQualifiedDomainName: switch i.Instance() { case 0: c.SGWNodeName = i case 1: c.SGSNNodeName = i case 2: c.MMENodeName = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.Indication: c.IndicationFlags = i case ie.TraceInformation: c.TraceInformation = i case ie.IPAddress: switch i.Instance() { case 0: c.S101IPAddress = i case 1: c.S102IPAddress = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.RFSPIndex: switch i.Instance() { case 0: c.SubscribedRFSPIndex = i case 1: c.RFSPIndexInUse = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.UETimeZone: c.UETimeZone = i case ie.LocalDistinguishedName: c.MMESGSNLDN = i case ie.MDTConfiguration: c.MDTConfiguration = i case ie.UserCSGInformation: c.UCI = i case ie.MonitoringEventInformation: c.MonitoringEventInformation = i case ie.IntegerNumber: switch i.Instance() { case 0: c.UEUsageType = i case 1: c.RemainingRunningServiceGapTimer = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.SCEFPDNConnection: c.SCEFPDNConnection = append(c.SCEFPDNConnection, i) case ie.RATType: c.RATType = i case ie.ServingPLMNRateControl: c.ServingPLMNRateControl = i case ie.Counter: c.MOExceptionDataCounter = i case ie.ExtendedTraceInformation: c.ExtendedTraceInformation = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *ContextResponse) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.Cause; ie != nil { l += ie.MarshalLen() } if ie := c.IMSI; ie != nil { l += ie.MarshalLen() } if ie := c.UEMMContext; ie != nil { l += ie.MarshalLen() } for _, ie := range c.UEPDNConnections { l += ie.MarshalLen() } if ie := c.SenderFTEID; ie != nil { l += ie.MarshalLen() } if ie := c.SGWS11S4FTEID; ie != nil { l += ie.MarshalLen() } if ie := c.SGWNodeName; ie != nil { l += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := c.TraceInformation; ie != nil { l += ie.MarshalLen() } if ie := c.S101IPAddress; ie != nil { l += ie.MarshalLen() } if ie := c.S102IPAddress; ie != nil { l += ie.MarshalLen() } if ie := c.SubscribedRFSPIndex; ie != nil { l += ie.MarshalLen() } if ie := c.RFSPIndexInUse; ie != nil { l += ie.MarshalLen() } if ie := c.UETimeZone; ie != nil { l += ie.MarshalLen() } if ie := c.MMESGSNLDN; ie != nil { l += ie.MarshalLen() } if ie := c.MDTConfiguration; ie != nil { l += ie.MarshalLen() } if ie := c.SGSNNodeName; ie != nil { l += ie.MarshalLen() } if ie := c.MMENodeName; ie != nil { l += ie.MarshalLen() } if ie := c.UCI; ie != nil { l += ie.MarshalLen() } if ie := c.MonitoringEventInformation; ie != nil { l += ie.MarshalLen() } if ie := c.UEUsageType; ie != nil { l += ie.MarshalLen() } for _, ie := range c.SCEFPDNConnection { l += ie.MarshalLen() } if ie := c.RATType; ie != nil { l += ie.MarshalLen() } if ie := c.ServingPLMNRateControl; ie != nil { l += ie.MarshalLen() } if ie := c.MOExceptionDataCounter; ie != nil { l += ie.MarshalLen() } if ie := c.RemainingRunningServiceGapTimer; ie != nil { l += ie.MarshalLen() } if ie := c.ExtendedTraceInformation; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *ContextResponse) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *ContextResponse) MessageTypeName() string { return "Context Response" } // TEID returns the TEID in uint32. func (c *ContextResponse) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/context-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ContextResponse into bytes. // // Deprecated: use ContextResponse.Marshal instead. func (c *ContextResponse) Serialize() ([]byte, error) { log.Println("ContextResponse.Serialize is deprecated. use ContextResponse.Marshal instead") return c.Marshal() } // SerializeTo serializes ContextResponse into bytes given as b. // // Deprecated: use ContextResponse.MarshalTo instead. func (c *ContextResponse) SerializeTo(b []byte) error { log.Println("ContextResponse.SerializeTo is deprecated. use ContextResponse.MarshalTo instead") return c.MarshalTo(b) } // DecodeContextResponse decodes bytes as ContextResponse. // // Deprecated: use ParseContextResponse instead. func DecodeContextResponse(b []byte) (*ContextResponse, error) { log.Println("DecodeContextResponse is deprecated. use ParseContextResponse instead") return ParseContextResponse(b) } // DecodeFromBytes decodes bytes as ContextResponse. // // Deprecated: use ContextResponse.UnmarshalBinary instead. func (c *ContextResponse) DecodeFromBytes(b []byte) error { log.Println("ContextResponse.DecodeFromBytes is deprecated. use ContextResponse.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of ContextResponse. // // Deprecated: use ContextResponse.MarshalLen instead. func (c *ContextResponse) Len() int { log.Println("ContextResponse.Len is deprecated. use ContextResponse.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv2/message/context-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestContextResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewContextResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewIMSI("123451234567890"), // ie.NewMMContext(), TODO: implement! ie.NewFullyQualifiedTEID(gtpv2.IFTypeS10MMEGTPC, 0xffffffff, "1.1.1.1", ""), ), Serialized: []byte{ // Header 0x48, 0x83, 0x00, 0x27, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, // F-TEID 0x57, 0x00, 0x09, 0x00, 0x8c, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseContextResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/create-bearer-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // CreateBearerRequest is a CreateBearerRequest Header and its IEs above. type CreateBearerRequest struct { *Header PTI *ie.IE LinkedEBI *ie.IE PCO *ie.IE BearerContexts []*ie.IE PGWFQCSID *ie.IE SGWFQCSID *ie.IE ChangeReportingAction *ie.IE CSGInformationReportingAction *ie.IE HeNBInformationReporting *ie.IE PresenceReportingAreaAction []*ie.IE IndicationFlags *ie.IE PGWNodeLoadControlInformation *ie.IE PGWAPNLoadControlInformation *ie.IE SGWNodeLoadControlInformation *ie.IE PGWOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE NBIFOMContainer *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewCreateBearerRequest creates a new CreateBearerRequest. func NewCreateBearerRequest(teid, seq uint32, ies ...*ie.IE) *CreateBearerRequest { c := &CreateBearerRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeCreateBearerRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.ProcedureTransactionID: c.PTI = i case ie.EPSBearerID: c.LinkedEBI = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.PGWFQCSID = i case 1: c.SGWFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.ChangeReportingAction: c.ChangeReportingAction = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.HeNBInformationReporting: c.HeNBInformationReporting = i case ie.PresenceReportingAreaAction: c.PresenceReportingAreaAction = append(c.PresenceReportingAreaAction, i) case ie.Indication: c.IndicationFlags = i case ie.LoadControlInformation: switch i.Instance() { case 0: c.PGWNodeLoadControlInformation = i case 1: c.PGWAPNLoadControlInformation = i case 2: c.SGWNodeLoadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.PGWOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FContainer: c.NBIFOMContainer = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes CreateBearerRequest into bytes. func (c *CreateBearerRequest) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes CreateBearerRequest into bytes. func (c *CreateBearerRequest) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.PTI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.LinkedEBI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.BearerContexts { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChangeReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.HeNBInformationReporting; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaAction { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWAPNLoadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseCreateBearerRequest decodes given bytes as CreateBearerRequest. func ParseCreateBearerRequest(b []byte) (*CreateBearerRequest, error) { c := &CreateBearerRequest{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as CreateBearerRequest. func (c *CreateBearerRequest) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { switch i.Type { case ie.ProcedureTransactionID: c.PTI = i case ie.EPSBearerID: c.LinkedEBI = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.PGWFQCSID = i case 1: c.SGWFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.ChangeReportingAction: c.ChangeReportingAction = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.HeNBInformationReporting: c.HeNBInformationReporting = i case ie.PresenceReportingAreaAction: c.PresenceReportingAreaAction = append(c.PresenceReportingAreaAction, i) case ie.Indication: c.IndicationFlags = i case ie.LoadControlInformation: switch i.Instance() { case 0: c.PGWNodeLoadControlInformation = i case 1: c.PGWAPNLoadControlInformation = i case 2: c.SGWNodeLoadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.PGWOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FContainer: c.NBIFOMContainer = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *CreateBearerRequest) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.PTI; ie != nil { l += ie.MarshalLen() } if ie := c.LinkedEBI; ie != nil { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } for _, ie := range c.BearerContexts { l += ie.MarshalLen() } if ie := c.PGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.ChangeReportingAction; ie != nil { l += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { l += ie.MarshalLen() } if ie := c.HeNBInformationReporting; ie != nil { l += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaAction { l += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := c.PGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.PGWAPNLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.SGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.PGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *CreateBearerRequest) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *CreateBearerRequest) MessageTypeName() string { return "Create Bearer Request" } // TEID returns the TEID in uint32. func (c *CreateBearerRequest) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/create-bearer-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes CreateBearerRequest into bytes. // // Deprecated: use CreateBearerRequest.Marshal instead. func (c *CreateBearerRequest) Serialize() ([]byte, error) { log.Println("CreateBearerRequest.Serialize is deprecated. use CreateBearerRequest.Marshal instead") return c.Marshal() } // SerializeTo serializes CreateBearerRequest into bytes given as b. // // Deprecated: use CreateBearerRequest.MarshalTo instead. func (c *CreateBearerRequest) SerializeTo(b []byte) error { log.Println("CreateBearerRequest.SerializeTo is deprecated. use CreateBearerRequest.MarshalTo instead") return c.MarshalTo(b) } // DecodeCreateBearerRequest decodes bytes as CreateBearerRequest. // // Deprecated: use ParseCreateBearerRequest instead. func DecodeCreateBearerRequest(b []byte) (*CreateBearerRequest, error) { log.Println("DecodeCreateBearerRequest is deprecated. use ParseCreateBearerRequest instead") return ParseCreateBearerRequest(b) } // DecodeFromBytes decodes bytes as CreateBearerRequest. // // Deprecated: use CreateBearerRequest.UnmarshalBinary instead. func (c *CreateBearerRequest) DecodeFromBytes(b []byte) error { log.Println("CreateBearerRequest.DecodeFromBytes is deprecated. use CreateBearerRequest.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of CreateBearerRequest. // // Deprecated: use CreateBearerRequest.MarshalLen instead. func (c *CreateBearerRequest) Len() int { log.Println("CreateBearerRequest.Len is deprecated. use CreateBearerRequest.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv2/message/create-bearer-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "net" "testing" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestCreateBearerRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewCreateBearerRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewEPSBearerID(5), ie.NewBearerContext( ie.NewEPSBearerID(0), ie.NewBearerQoS(1, 2, 1, 0xff, 0x1111111111, 0x2222222222, 0x1111111111, 0x2222222222), ie.NewBearerTFTCreateNewTFT( []*ie.TFTPacketFilter{ ie.NewTFTPacketFilter( ie.TFTPFPreRel7TFTFilter, 1, 0, ie.NewTFTPFComponentIPv4RemoteAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv4LocalAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv6RemoteAddress( net.ParseIP("2001::1"), net.CIDRMask(64, 128), ), ie.NewTFTPFComponentIPv6RemoteAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ie.NewTFTPFComponentIPv6LocalAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ), ie.NewTFTPacketFilter( ie.TFTPFDownlinkOnly, 2, 0, ie.NewTFTPFComponentProtocolIdentifierNextHeader(1), ie.NewTFTPFComponentSingleLocalPort(2152), ie.NewTFTPFComponentSingleRemotePort(2123), ie.NewTFTPFComponentLocalPortRange(2123, 2152), ie.NewTFTPFComponentRemotePortRange(2152, 2123), ), ie.NewTFTPacketFilter( ie.TFTPFUplinkOnly, 3, 0, ie.NewTFTPFComponentSecurityParameterIndex(0xdeadbeef), ie.NewTFTPFComponentTypeOfServiceTrafficClass(1, 2), ie.NewTFTPFComponentFlowLabel(0x00011111), ie.NewTFTPFComponentDestinationMACAddress(mac1), ie.NewTFTPFComponentSourceMACAddress(mac2), ), ie.NewTFTPacketFilter( ie.TFTPFBidirectional, 4, 0, ie.NewTFTPFComponentDot1QCTAGVID(0x0111), ie.NewTFTPFComponentDot1QSTAGVID(0x0222), ie.NewTFTPFComponentDot1QCTAGPCPDEI(3), ie.NewTFTPFComponentDot1QSTAGPCPDEI(5), ie.NewTFTPFComponentEthertype(0x0800), ), }, []*ie.TFTParameter{ ie.NewTFTParameter(ie.TFTParamIDAuthorizationToken, []byte{0xde, 0xad, 0xbe, 0xef}), ie.NewTFTParameter(ie.TFTParamIDFlowIdentifier, []byte{0x11, 0x11, 0x22, 0x22}), ie.NewTFTParameter(ie.TFTParamIDPacketFileterIdentifier, []byte{0x01, 0x02, 0x03, 0x04}), }, ), ), ie.NewBearerContext( ie.NewEPSBearerID(6), ie.NewBearerQoS(1, 2, 1, 0xff, 0x1111111111, 0x2222222222, 0x1111111111, 0x2222222222), ie.NewBearerTFTCreateNewTFT( []*ie.TFTPacketFilter{ ie.NewTFTPacketFilter( ie.TFTPFPreRel7TFTFilter, 1, 0, ie.NewTFTPFComponentIPv4RemoteAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv4LocalAddress( net.ParseIP("127.0.0.1"), net.IPv4Mask(255, 255, 255, 0), ), ie.NewTFTPFComponentIPv6RemoteAddress( net.ParseIP("2001::1"), net.CIDRMask(64, 128), ), ie.NewTFTPFComponentIPv6RemoteAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ie.NewTFTPFComponentIPv6LocalAddressPrefixLength( net.ParseIP("2001::1"), 64, ), ), ie.NewTFTPacketFilter( ie.TFTPFDownlinkOnly, 2, 0, ie.NewTFTPFComponentProtocolIdentifierNextHeader(1), ie.NewTFTPFComponentSingleLocalPort(2152), ie.NewTFTPFComponentSingleRemotePort(2123), ie.NewTFTPFComponentLocalPortRange(2123, 2152), ie.NewTFTPFComponentRemotePortRange(2152, 2123), ), ie.NewTFTPacketFilter( ie.TFTPFUplinkOnly, 3, 0, ie.NewTFTPFComponentSecurityParameterIndex(0xdeadbeef), ie.NewTFTPFComponentTypeOfServiceTrafficClass(1, 2), ie.NewTFTPFComponentFlowLabel(0x00011111), ie.NewTFTPFComponentDestinationMACAddress(mac1), ie.NewTFTPFComponentSourceMACAddress(mac2), ), ie.NewTFTPacketFilter( ie.TFTPFBidirectional, 4, 0, ie.NewTFTPFComponentDot1QCTAGVID(0x0111), ie.NewTFTPFComponentDot1QSTAGVID(0x0222), ie.NewTFTPFComponentDot1QCTAGPCPDEI(3), ie.NewTFTPFComponentDot1QSTAGPCPDEI(5), ie.NewTFTPFComponentEthertype(0x0800), ), }, []*ie.TFTParameter{ ie.NewTFTParameter(ie.TFTParamIDAuthorizationToken, []byte{0xde, 0xad, 0xbe, 0xef}), ie.NewTFTParameter(ie.TFTParamIDFlowIdentifier, []byte{0x11, 0x11, 0x22, 0x22}), ie.NewTFTParameter(ie.TFTParamIDPacketFileterIdentifier, []byte{0x01, 0x02, 0x03, 0x04}), }, ), ), ), Serialized: []byte{ // Header 0x48, 0x5f, 0x01, 0xb9, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // BearerContext 1 0x5d, 0x00, 0xd2, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x00, // BearerQoS 0x50, 0x00, 0x16, 0x00, 0x49, 0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, // BearerTFT 0x54, 0x00, 0xaf, 0x00, 0x34, 0x01, 0x00, 0x57, 0x10, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x11, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x23, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x12, 0x00, 0x12, 0x30, 0x01, 0x40, 0x08, 0x68, 0x50, 0x08, 0x4b, 0x41, 0x08, 0x4b, 0x08, 0x68, 0x51, 0x08, 0x68, 0x08, 0x4b, 0x23, 0x00, 0x1a, 0x60, 0xde, 0xad, 0xbe, 0xef, 0x70, 0x01, 0x02, 0x80, 0x01, 0x11, 0x11, 0x81, 0x12, 0x34, 0x56, 0x78, 0x90, 0x01, 0x82, 0x12, 0x34, 0x56, 0x78, 0x90, 0x02, 0x34, 0x00, 0x0d, 0x83, 0x01, 0x11, 0x84, 0x02, 0x22, 0x85, 0x03, 0x86, 0x05, 0x87, 0x08, 0x00, 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x02, 0x04, 0x11, 0x11, 0x22, 0x22, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, // BearerContext 2 0x5d, 0x00, 0xd2, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // BearerQoS 0x50, 0x00, 0x16, 0x00, 0x49, 0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, // BearerTFT 0x54, 0x00, 0xaf, 0x00, 0x34, 0x01, 0x00, 0x57, 0x10, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x11, 0x7f, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x23, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x12, 0x00, 0x12, 0x30, 0x01, 0x40, 0x08, 0x68, 0x50, 0x08, 0x4b, 0x41, 0x08, 0x4b, 0x08, 0x68, 0x51, 0x08, 0x68, 0x08, 0x4b, 0x23, 0x00, 0x1a, 0x60, 0xde, 0xad, 0xbe, 0xef, 0x70, 0x01, 0x02, 0x80, 0x01, 0x11, 0x11, 0x81, 0x12, 0x34, 0x56, 0x78, 0x90, 0x01, 0x82, 0x12, 0x34, 0x56, 0x78, 0x90, 0x02, 0x34, 0x00, 0x0d, 0x83, 0x01, 0x11, 0x84, 0x02, 0x22, 0x85, 0x03, 0x86, 0x05, 0x87, 0x08, 0x00, 0x01, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x02, 0x04, 0x11, 0x11, 0x22, 0x22, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseCreateBearerRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/create-bearer-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // CreateBearerResponse is a CreateBearerResponse Header and its IEs above. type CreateBearerResponse struct { *Header Cause *ie.IE BearerContexts []*ie.IE Recovery *ie.IE MMEFQCSID *ie.IE SGWFQCSID *ie.IE EPDGFQCSID *ie.IE TWANFQCSID *ie.IE PCO *ie.IE UETimeZone *ie.IE ULI *ie.IE TWANIdentifier *ie.IE MMEOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE PresenceReportingAction []*ie.IE MMESGSNIdentifier *ie.IE TWANePDGOverloadControlInformation *ie.IE WLANLocationInformation *ie.IE WLANLocationTimestamp *ie.IE UELocalIPAddress *ie.IE UEUDPPort *ie.IE NBIFOMContainer *ie.IE UETCPPort *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewCreateBearerResponse creates a new CreateBearerResponse. func NewCreateBearerResponse(teid, seq uint32, ies ...*ie.IE) *CreateBearerResponse { c := &CreateBearerResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeCreateBearerResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.Recovery: c.Recovery = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.MMEFQCSID = i case 1: c.SGWFQCSID = i case 2: c.EPDGFQCSID = i case 3: c.TWANFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.ProtocolConfigurationOptions: c.PCO = i case ie.UETimeZone: c.UETimeZone = i case ie.UserLocationInformation: c.ULI = i case ie.TWANIdentifier: switch i.Instance() { case 0: c.TWANIdentifier = i case 1: c.WLANLocationInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.TWANIdentifierTimestamp: switch i.Instance() { case 1: c.WLANLocationTimestamp = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.MMEOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i case 2: c.TWANePDGOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PresenceReportingAreaAction: c.PresenceReportingAction = append(c.PresenceReportingAction, i) case ie.IPAddress: switch i.Instance() { case 0: c.MMESGSNIdentifier = i case 1: c.UELocalIPAddress = i } case ie.PortNumber: switch i.Instance() { case 0: c.UEUDPPort = i case 1: c.UETCPPort = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FContainer: c.NBIFOMContainer = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes CreateBearerResponse into bytes. func (c *CreateBearerResponse) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes CreateBearerResponse into bytes. func (c *CreateBearerResponse) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.BearerContexts { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Recovery; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMEFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EPDGFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UETimeZone; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ULI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANIdentifier; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMEOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.PresenceReportingAction { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMESGSNIdentifier; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANePDGOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.WLANLocationInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.WLANLocationTimestamp; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UELocalIPAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UEUDPPort; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UETCPPort; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseCreateBearerResponse decodes given bytes as CreateBearerResponse. func ParseCreateBearerResponse(b []byte) (*CreateBearerResponse, error) { c := &CreateBearerResponse{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as CreateBearerResponse. func (c *CreateBearerResponse) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.Recovery: c.Recovery = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.MMEFQCSID = i case 1: c.SGWFQCSID = i case 2: c.EPDGFQCSID = i case 3: c.TWANFQCSID = i } case ie.ProtocolConfigurationOptions: c.PCO = i case ie.UETimeZone: c.UETimeZone = i case ie.UserLocationInformation: c.ULI = i case ie.TWANIdentifier: switch i.Instance() { case 0: c.TWANIdentifier = i case 1: c.WLANLocationInformation = i } case ie.TWANIdentifierTimestamp: if i.Instance() == 1 { c.WLANLocationTimestamp = i } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.MMEOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i case 2: c.TWANePDGOverloadControlInformation = i } case ie.PresenceReportingAreaAction: c.PresenceReportingAction = append(c.PresenceReportingAction, i) case ie.IPAddress: switch i.Instance() { case 0: c.MMESGSNIdentifier = i case 1: c.UELocalIPAddress = i } case ie.PortNumber: switch i.Instance() { case 0: c.UEUDPPort = i case 1: c.UETCPPort = i } case ie.FContainer: c.NBIFOMContainer = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *CreateBearerResponse) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.Cause; ie != nil { l += ie.MarshalLen() } for _, ie := range c.BearerContexts { l += ie.MarshalLen() } if ie := c.Recovery; ie != nil { l += ie.MarshalLen() } if ie := c.MMEFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.EPDGFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.TWANFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } if ie := c.UETimeZone; ie != nil { l += ie.MarshalLen() } if ie := c.ULI; ie != nil { l += ie.MarshalLen() } if ie := c.TWANIdentifier; ie != nil { l += ie.MarshalLen() } if ie := c.MMEOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } for _, ie := range c.PresenceReportingAction { l += ie.MarshalLen() } if ie := c.MMESGSNIdentifier; ie != nil { l += ie.MarshalLen() } if ie := c.TWANePDGOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.WLANLocationInformation; ie != nil { l += ie.MarshalLen() } if ie := c.WLANLocationTimestamp; ie != nil { l += ie.MarshalLen() } if ie := c.UELocalIPAddress; ie != nil { l += ie.MarshalLen() } if ie := c.UEUDPPort; ie != nil { l += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { l += ie.MarshalLen() } if ie := c.UETCPPort; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *CreateBearerResponse) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *CreateBearerResponse) MessageTypeName() string { return "Create Bearer Response" } // TEID returns the TEID in uint32. func (c *CreateBearerResponse) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/create-bearer-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes CreateBearerResponse into bytes. // // Deprecated: use CreateBearerResponse.Marshal instead. func (c *CreateBearerResponse) Serialize() ([]byte, error) { log.Println("CreateBearerResponse.Serialize is deprecated. use CreateBearerResponse.Marshal instead") return c.Marshal() } // SerializeTo serializes CreateBearerResponse into bytes given as b. // // Deprecated: use CreateBearerResponse.MarshalTo instead. func (c *CreateBearerResponse) SerializeTo(b []byte) error { log.Println("CreateBearerResponse.SerializeTo is deprecated. use CreateBearerResponse.MarshalTo instead") return c.MarshalTo(b) } // DecodeCreateBearerResponse decodes bytes as CreateBearerResponse. // // Deprecated: use ParseCreateBearerResponse instead. func DecodeCreateBearerResponse(b []byte) (*CreateBearerResponse, error) { log.Println("DecodeCreateBearerResponse is deprecated. use ParseCreateBearerResponse instead") return ParseCreateBearerResponse(b) } // DecodeFromBytes decodes bytes as CreateBearerResponse. // // Deprecated: use CreateBearerResponse.UnmarshalBinary instead. func (c *CreateBearerResponse) DecodeFromBytes(b []byte) error { log.Println("CreateBearerResponse.DecodeFromBytes is deprecated. use CreateBearerResponse.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of CreateBearerResponse. // // Deprecated: use CreateBearerResponse.MarshalLen instead. func (c *CreateBearerResponse) Len() int { log.Println("CreateBearerResponse.Len is deprecated. use CreateBearerResponse.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv2/message/create-bearer-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestCreateBearerResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewCreateBearerResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewBearerContext( ie.NewEPSBearerID(0), ie.NewBearerQoS(1, 2, 1, 0xff, 0x1111111111, 0x2222222222, 0x1111111111, 0x2222222222), ), ie.NewBearerContext( ie.NewEPSBearerID(6), ie.NewBearerQoS(1, 2, 1, 0xff, 0x1111111111, 0x2222222222, 0x1111111111, 0x2222222222), ), ), Serialized: []byte{ // Header 0x48, 0x60, 0x00, 0x54, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // BearerContext 1 0x5d, 0x00, 0x1f, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x00, // BearerQoS 0x50, 0x00, 0x16, 0x00, 0x49, 0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, // BearerContext 2 0x5d, 0x00, 0x1f, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // BearerQoS 0x50, 0x00, 0x16, 0x00, 0x49, 0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseCreateBearerResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/create-session-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // CreateSessionRequest is a CreateSessionRequest Header and its IEs above. type CreateSessionRequest struct { *Header IMSI *ie.IE MSISDN *ie.IE MEI *ie.IE ULI *ie.IE ServingNetwork *ie.IE RATType *ie.IE IndicationFlags *ie.IE SenderFTEIDC *ie.IE PGWS5S8FTEIDC *ie.IE APN *ie.IE SelectionMode *ie.IE PDNType *ie.IE PAA *ie.IE APNRestriction *ie.IE AMBR *ie.IE LinkedEBI *ie.IE TWMI *ie.IE PCO *ie.IE BearerContextsToBeCreated []*ie.IE BearerContextsToBeRemoved []*ie.IE TraceInformation *ie.IE Recovery *ie.IE MMEFQCSID *ie.IE SGWFQCSID *ie.IE EPDGFQCSID *ie.IE TWANFQCSID *ie.IE UETimeZone *ie.IE UCI *ie.IE ChargingCharacteristics *ie.IE MMESGSNLDN *ie.IE SGWLDN *ie.IE EPDGLDN *ie.IE TWANLDN *ie.IE SignallingPriorityIndication *ie.IE UELocalIPAddress *ie.IE UEUDPPort *ie.IE APCO *ie.IE HeNBLocalIPAddress *ie.IE HeNBUDPPort *ie.IE MMESGSNIdentifier *ie.IE TWANIdentifier *ie.IE EPDGIPAddress *ie.IE CNOperatorSelectionEntity *ie.IE PresenceReportingAreaInformation []*ie.IE MMESGSNOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE TWANePDGOverloadControlInformation *ie.IE OriginationTimeStamp *ie.IE MaximumWaitTime *ie.IE WLANLocationInformation *ie.IE WLANLocationTimeStamp *ie.IE NBIFOMContainer *ie.IE RemoteUEContextConnected []*ie.IE TGPPAAAServerIdentifier *ie.IE EPCO *ie.IE ServingPLMNRateControl *ie.IE MOExceptionDataCounter *ie.IE UETCPPort *ie.IE MappedUEUsageType *ie.IE ULIForSGW *ie.IE SGWUNodeName *ie.IE SecondaryRATUsageDataReport []*ie.IE UPFunctionSelectionIndicationFlags *ie.IE APNRateControlStatus *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewCreateSessionRequest creates a new CreateSessionRequest. func NewCreateSessionRequest(teid, seq uint32, ies ...*ie.IE) *CreateSessionRequest { c := &CreateSessionRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeCreateSessionRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.MSISDN: c.MSISDN = i case ie.MobileEquipmentIdentity: c.MEI = i case ie.UserLocationInformation: switch i.Instance() { case 0: c.ULI = i case 1: c.ULIForSGW = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.ServingNetwork: c.ServingNetwork = i case ie.RATType: c.RATType = i case ie.Indication: c.IndicationFlags = i case ie.FullyQualifiedTEID: switch i.Instance() { case 0: c.SenderFTEIDC = i case 1: c.PGWS5S8FTEIDC = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.AccessPointName: c.APN = i case ie.SelectionMode: c.SelectionMode = i case ie.PDNType: c.PDNType = i case ie.PDNAddressAllocation: c.PAA = i case ie.APNRestriction: c.APNRestriction = i case ie.AggregateMaximumBitRate: c.AMBR = i case ie.EPSBearerID: c.LinkedEBI = i case ie.TrustedWLANModeIndication: c.TWMI = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.BearerContext: switch i.Instance() { case 0: c.BearerContextsToBeCreated = append(c.BearerContextsToBeCreated, i) case 1: c.BearerContextsToBeRemoved = append(c.BearerContextsToBeRemoved, i) default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.MMEFQCSID = i case 1: c.SGWFQCSID = i case 2: c.EPDGFQCSID = i case 3: c.TWANFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.TraceInformation: c.TraceInformation = i case ie.Recovery: c.Recovery = i case ie.UETimeZone: c.UETimeZone = i case ie.UserCSGInformation: c.UCI = i case ie.ChargingCharacteristics: c.ChargingCharacteristics = i case ie.LocalDistinguishedName: switch i.Instance() { case 0: c.MMESGSNLDN = i case 1: c.SGWLDN = i case 2: c.EPDGLDN = i case 3: c.TWANLDN = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.SignallingPriorityIndication: c.SignallingPriorityIndication = i case ie.IPAddress: switch i.Instance() { case 0: c.UELocalIPAddress = i case 1: c.HeNBLocalIPAddress = i case 2: c.MMESGSNIdentifier = i case 3: c.EPDGIPAddress = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PortNumber: switch i.Instance() { case 0: c.UEUDPPort = i case 1: c.HeNBUDPPort = i case 2: c.UETCPPort = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.AdditionalProtocolConfigurationOptions: c.APCO = i case ie.TWANIdentifier: switch i.Instance() { case 0: c.TWANIdentifier = i case 1: c.WLANLocationInformation = i } case ie.CNOperatorSelectionEntity: c.CNOperatorSelectionEntity = i case ie.PresenceReportingAreaInformation: c.PresenceReportingAreaInformation = append(c.PresenceReportingAreaInformation, i) case ie.OverloadControlInformation: switch i.Instance() { case 0: c.MMESGSNOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i case 2: c.TWANePDGOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.MillisecondTimeStamp: c.OriginationTimeStamp = i case ie.IntegerNumber: c.MaximumWaitTime = i case ie.TWANIdentifierTimestamp: c.WLANLocationTimeStamp = i case ie.FContainer: c.NBIFOMContainer = i case ie.RemoteUEContext: c.RemoteUEContextConnected = append(c.RemoteUEContextConnected, i) case ie.NodeIdentifier: c.TGPPAAAServerIdentifier = i case ie.ExtendedProtocolConfigurationOptions: c.EPCO = i case ie.ServingPLMNRateControl: c.ServingPLMNRateControl = i case ie.Counter: c.MOExceptionDataCounter = i case ie.MappedUEUsageType: c.MappedUEUsageType = i case ie.FullyQualifiedDomainName: c.SGWUNodeName = i case ie.SecondaryRATUsageDataReport: c.SecondaryRATUsageDataReport = append(c.SecondaryRATUsageDataReport, i) case ie.UPFunctionSelectionIndicationFlags: c.UPFunctionSelectionIndicationFlags = i case ie.APNRateControlStatus: c.APNRateControlStatus = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes CreateSessionRequest into bytes. func (c *CreateSessionRequest) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes CreateSessionRequest into bytes. func (c *CreateSessionRequest) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.IMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MSISDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MEI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ULI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ServingNetwork; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RATType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SenderFTEIDC; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWS5S8FTEIDC; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SelectionMode; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PDNType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PAA; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APNRestriction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.AMBR; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.LinkedEBI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWMI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.BearerContextsToBeCreated { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.BearerContextsToBeRemoved { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TraceInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Recovery; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMEFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EPDGFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UETimeZone; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UCI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChargingCharacteristics; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMESGSNLDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWLDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EPDGLDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANLDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SignallingPriorityIndication; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UELocalIPAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UEUDPPort; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.HeNBLocalIPAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.HeNBUDPPort; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMESGSNIdentifier; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANIdentifier; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EPDGIPAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CNOperatorSelectionEntity; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaInformation { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMESGSNOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANePDGOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.OriginationTimeStamp; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MaximumWaitTime; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.WLANLocationInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.WLANLocationTimeStamp; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.RemoteUEContextConnected { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TGPPAAAServerIdentifier; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EPCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ServingPLMNRateControl; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MOExceptionDataCounter; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UETCPPort; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MappedUEUsageType; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ULIForSGW; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWUNodeName; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.SecondaryRATUsageDataReport { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UPFunctionSelectionIndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APNRateControlStatus; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseCreateSessionRequest decodes given bytes as CreateSessionRequest. func ParseCreateSessionRequest(b []byte) (*CreateSessionRequest, error) { c := &CreateSessionRequest{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as CreateSessionRequest. func (c *CreateSessionRequest) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.MSISDN: c.MSISDN = i case ie.MobileEquipmentIdentity: c.MEI = i case ie.UserLocationInformation: switch i.Instance() { case 0: c.ULI = i case 1: c.ULIForSGW = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.ServingNetwork: c.ServingNetwork = i case ie.RATType: c.RATType = i case ie.Indication: c.IndicationFlags = i case ie.FullyQualifiedTEID: switch i.Instance() { case 0: c.SenderFTEIDC = i case 1: c.PGWS5S8FTEIDC = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.AccessPointName: c.APN = i case ie.SelectionMode: c.SelectionMode = i case ie.PDNType: c.PDNType = i case ie.PDNAddressAllocation: c.PAA = i case ie.APNRestriction: c.APNRestriction = i case ie.AggregateMaximumBitRate: c.AMBR = i case ie.EPSBearerID: c.LinkedEBI = i case ie.TrustedWLANModeIndication: c.TWMI = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.BearerContext: switch i.Instance() { case 0: c.BearerContextsToBeCreated = append(c.BearerContextsToBeCreated, i) case 1: c.BearerContextsToBeRemoved = append(c.BearerContextsToBeRemoved, i) default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.MMEFQCSID = i case 1: c.SGWFQCSID = i case 2: c.EPDGFQCSID = i case 3: c.TWANFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.TraceInformation: c.TraceInformation = i case ie.Recovery: c.Recovery = i case ie.UETimeZone: c.UETimeZone = i case ie.UserCSGInformation: c.UCI = i case ie.ChargingCharacteristics: c.ChargingCharacteristics = i case ie.LocalDistinguishedName: switch i.Instance() { case 0: c.MMESGSNLDN = i case 1: c.SGWLDN = i case 2: c.EPDGLDN = i case 3: c.TWANLDN = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.SignallingPriorityIndication: c.SignallingPriorityIndication = i case ie.IPAddress: switch i.Instance() { case 0: c.UELocalIPAddress = i case 1: c.HeNBLocalIPAddress = i case 2: c.MMESGSNIdentifier = i case 3: c.EPDGIPAddress = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PortNumber: switch i.Instance() { case 0: c.UEUDPPort = i case 1: c.HeNBUDPPort = i case 2: c.UETCPPort = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.AdditionalProtocolConfigurationOptions: c.APCO = i case ie.TWANIdentifier: switch i.Instance() { case 0: c.TWANIdentifier = i case 1: c.WLANLocationInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.CNOperatorSelectionEntity: c.CNOperatorSelectionEntity = i case ie.PresenceReportingAreaInformation: c.PresenceReportingAreaInformation = append(c.PresenceReportingAreaInformation, i) case ie.OverloadControlInformation: switch i.Instance() { case 0: c.MMESGSNOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i case 2: c.TWANePDGOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.MillisecondTimeStamp: c.OriginationTimeStamp = i case ie.IntegerNumber: c.MaximumWaitTime = i case ie.TWANIdentifierTimestamp: c.WLANLocationTimeStamp = i case ie.FContainer: c.NBIFOMContainer = i case ie.RemoteUEContext: c.RemoteUEContextConnected = append(c.RemoteUEContextConnected, i) case ie.NodeIdentifier: c.TGPPAAAServerIdentifier = i case ie.ExtendedProtocolConfigurationOptions: c.EPCO = i case ie.ServingPLMNRateControl: c.ServingPLMNRateControl = i case ie.Counter: c.MOExceptionDataCounter = i case ie.MappedUEUsageType: c.MappedUEUsageType = i case ie.FullyQualifiedDomainName: c.SGWUNodeName = i case ie.SecondaryRATUsageDataReport: c.SecondaryRATUsageDataReport = append(c.SecondaryRATUsageDataReport, i) case ie.UPFunctionSelectionIndicationFlags: c.UPFunctionSelectionIndicationFlags = i case ie.APNRateControlStatus: c.APNRateControlStatus = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *CreateSessionRequest) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.IMSI; ie != nil { l += ie.MarshalLen() } if ie := c.MSISDN; ie != nil { l += ie.MarshalLen() } if ie := c.MEI; ie != nil { l += ie.MarshalLen() } if ie := c.ULI; ie != nil { l += ie.MarshalLen() } if ie := c.ServingNetwork; ie != nil { l += ie.MarshalLen() } if ie := c.RATType; ie != nil { l += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := c.SenderFTEIDC; ie != nil { l += ie.MarshalLen() } if ie := c.PGWS5S8FTEIDC; ie != nil { l += ie.MarshalLen() } if ie := c.APN; ie != nil { l += ie.MarshalLen() } if ie := c.SelectionMode; ie != nil { l += ie.MarshalLen() } if ie := c.PDNType; ie != nil { l += ie.MarshalLen() } if ie := c.PAA; ie != nil { l += ie.MarshalLen() } if ie := c.APNRestriction; ie != nil { l += ie.MarshalLen() } if ie := c.AMBR; ie != nil { l += ie.MarshalLen() } if ie := c.LinkedEBI; ie != nil { l += ie.MarshalLen() } if ie := c.TWMI; ie != nil { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } for _, ie := range c.BearerContextsToBeCreated { l += ie.MarshalLen() } for _, ie := range c.BearerContextsToBeRemoved { l += ie.MarshalLen() } if ie := c.TraceInformation; ie != nil { l += ie.MarshalLen() } if ie := c.Recovery; ie != nil { l += ie.MarshalLen() } if ie := c.MMEFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.EPDGFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.TWANFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.UETimeZone; ie != nil { l += ie.MarshalLen() } if ie := c.UCI; ie != nil { l += ie.MarshalLen() } if ie := c.ChargingCharacteristics; ie != nil { l += ie.MarshalLen() } if ie := c.MMESGSNLDN; ie != nil { l += ie.MarshalLen() } if ie := c.SGWLDN; ie != nil { l += ie.MarshalLen() } if ie := c.EPDGLDN; ie != nil { l += ie.MarshalLen() } if ie := c.TWANLDN; ie != nil { l += ie.MarshalLen() } if ie := c.SignallingPriorityIndication; ie != nil { l += ie.MarshalLen() } if ie := c.UELocalIPAddress; ie != nil { l += ie.MarshalLen() } if ie := c.UEUDPPort; ie != nil { l += ie.MarshalLen() } if ie := c.APCO; ie != nil { l += ie.MarshalLen() } if ie := c.HeNBLocalIPAddress; ie != nil { l += ie.MarshalLen() } if ie := c.HeNBUDPPort; ie != nil { l += ie.MarshalLen() } if ie := c.MMESGSNIdentifier; ie != nil { l += ie.MarshalLen() } if ie := c.TWANIdentifier; ie != nil { l += ie.MarshalLen() } if ie := c.EPDGIPAddress; ie != nil { l += ie.MarshalLen() } if ie := c.CNOperatorSelectionEntity; ie != nil { l += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaInformation { l += ie.MarshalLen() } if ie := c.MMESGSNOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.TWANePDGOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.OriginationTimeStamp; ie != nil { l += ie.MarshalLen() } if ie := c.MaximumWaitTime; ie != nil { l += ie.MarshalLen() } if ie := c.WLANLocationInformation; ie != nil { l += ie.MarshalLen() } if ie := c.WLANLocationTimeStamp; ie != nil { l += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { l += ie.MarshalLen() } for _, ie := range c.RemoteUEContextConnected { l += ie.MarshalLen() } if ie := c.TGPPAAAServerIdentifier; ie != nil { l += ie.MarshalLen() } if ie := c.EPCO; ie != nil { l += ie.MarshalLen() } if ie := c.ServingPLMNRateControl; ie != nil { l += ie.MarshalLen() } if ie := c.MOExceptionDataCounter; ie != nil { l += ie.MarshalLen() } if ie := c.UETCPPort; ie != nil { l += ie.MarshalLen() } if ie := c.MappedUEUsageType; ie != nil { l += ie.MarshalLen() } if ie := c.ULIForSGW; ie != nil { l += ie.MarshalLen() } if ie := c.SGWUNodeName; ie != nil { l += ie.MarshalLen() } for _, ie := range c.SecondaryRATUsageDataReport { l += ie.MarshalLen() } if ie := c.UPFunctionSelectionIndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := c.APNRateControlStatus; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *CreateSessionRequest) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *CreateSessionRequest) MessageTypeName() string { return "Create Session Request" } // TEID returns the TEID in uint32. func (c *CreateSessionRequest) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/create-session-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes CreateSessionRequest into bytes. // // Deprecated: use CreateSessionRequest.Marshal instead. func (c *CreateSessionRequest) Serialize() ([]byte, error) { log.Println("CreateSessionRequest.Serialize is deprecated. use CreateSessionRequest.Marshal instead") return c.Marshal() } // SerializeTo serializes CreateSessionRequest into bytes given as b. // // Deprecated: use CreateSessionRequest.MarshalTo instead. func (c *CreateSessionRequest) SerializeTo(b []byte) error { log.Println("CreateSessionRequest.SerializeTo is deprecated. use CreateSessionRequest.MarshalTo instead") return c.MarshalTo(b) } // DecodeCreateSessionRequest decodes bytes as CreateSessionRequest. // // Deprecated: use ParseCreateSessionRequest instead. func DecodeCreateSessionRequest(b []byte) (*CreateSessionRequest, error) { log.Println("DecodeCreateSessionRequest is deprecated. use ParseCreateSessionRequest instead") return ParseCreateSessionRequest(b) } // DecodeFromBytes decodes bytes as CreateSessionRequest. // // Deprecated: use CreateSessionRequest.UnmarshalBinary instead. func (c *CreateSessionRequest) DecodeFromBytes(b []byte) error { log.Println("CreateSessionRequest.DecodeFromBytes is deprecated. use CreateSessionRequest.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of CreateSessionRequest. // // Deprecated: use CreateSessionRequest.MarshalLen instead. func (c *CreateSessionRequest) Len() int { log.Println("CreateSessionRequest.Len is deprecated. use CreateSessionRequest.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv2/message/create-session-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestCreateSessionRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/FromMMEtoSGW", Structured: message.NewCreateSessionRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIMSI("123451234567890"), ie.NewMSISDN("123450123456789"), ie.NewAccessPointName("some.apn.example"), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11MMEGTPC, 0xffffffff, "1.1.1.1", ""), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, 0xffffffff, "1.1.1.2", "").WithInstance(1), ie.NewPDNType(gtpv2.PDNTypeIPv4), ie.NewAggregateMaximumBitRate(0x11111111, 0x22222222), ie.NewIndicationFromOctets(0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40), ie.NewBearerContext( ie.NewEPSBearerID(0x05), ie.NewBearerQoS(1, 2, 1, 0xff, 0x1111111111, 0x2222222222, 0x1111111111, 0x2222222222), ), ie.NewBearerContext( ie.NewEPSBearerID(0x06), ie.NewBearerQoS(1, 2, 1, 0xff, 0x1111111111, 0x2222222222, 0x1111111111, 0x2222222222), ), ie.NewMobileEquipmentIdentity("123450123456789"), ie.NewServingNetwork("123", "45"), ie.NewPDNAddressAllocation("2.2.2.2"), ie.NewAPNRestriction(gtpv2.APNRestrictionPublic1), ie.NewUserLocationInformationStruct( nil, nil, nil, ie.NewTAI("123", "45", 0x0001), ie.NewECGI("123", "45", 0x00000101), nil, nil, nil, ), ie.NewRATType(gtpv2.RATTypeEUTRAN), ie.NewSelectionMode(gtpv2.SelectionModeMSorNetworkProvidedAPNSubscribedVerified), ), Serialized: []byte{ // Header 0x48, 0x20, 0x00, 0xed, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, // MSISDN 0x4c, 0x00, 0x08, 0x00, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9, // MEI 0x4b, 0x00, 0x08, 0x00, 0x21, 0x43, 0x05, 0x21, 0x43, 0x65, 0x87, 0xf9, // ULI: TAI ECGI 0x56, 0x00, 0x0d, 0x00, 0x18, 0x21, 0xf3, 0x54, 0x00, 0x01, 0x21, 0xf3, 0x54, 0x00, 0x00, 0x01, 0x01, // ServingNetwork 0x53, 0x00, 0x03, 0x00, 0x21, 0xf3, 0x54, // RATType 0x52, 0x00, 0x01, 0x00, 0x06, // Indication 0x4d, 0x00, 0x07, 0x00, 0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40, // F-TEID S11 0x57, 0x00, 0x09, 0x00, 0x8a, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, // F-TEID S5/S8 0x57, 0x00, 0x09, 0x01, 0x87, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x02, // APN 0x47, 0x00, 0x11, 0x00, 0x04, 0x73, 0x6f, 0x6d, 0x65, 0x03, 0x61, 0x70, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, // SelectionMode 0x80, 0x00, 0x01, 0x00, 0x00, // PDNType 0x63, 0x00, 0x01, 0x00, 0x01, // PAA 0x4f, 0x00, 0x05, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, // APNRestriction 0x7f, 0x00, 0x01, 0x00, 0x01, // AMBR 0x48, 0x00, 0x08, 0x00, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, // BearerContext 1 0x5d, 0x00, 0x1f, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // BearerQoS 0x50, 0x00, 0x16, 0x00, 0x49, 0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, // BearerContext 2 0x5d, 0x00, 0x1f, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // BearerQoS 0x50, 0x00, 0x16, 0x00, 0x49, 0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseCreateSessionRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/create-session-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // CreateSessionResponse is a CreateSessionResponse Header and its IEs above. type CreateSessionResponse struct { *Header Cause *ie.IE ChangeReportingAction *ie.IE CSGInformationReportingAction *ie.IE HeNBInformationReporting *ie.IE SenderFTEIDC *ie.IE PGWS5S8FTEIDC *ie.IE PAA *ie.IE APNRestriction *ie.IE AMBR *ie.IE EBI *ie.IE PCO *ie.IE BearerContextsCreated []*ie.IE BearerContextMarkedForRemoval *ie.IE Recovery *ie.IE ChargingGatewayName *ie.IE // = PGWNodeName ChargingGatewayAddress *ie.IE PGWFQCSID *ie.IE SGWFQCSID *ie.IE SGWLDN *ie.IE PGWLDN *ie.IE PGWBackOffTime *ie.IE APCO *ie.IE TrustedTWANIPv4Parameters *ie.IE IndicationFlags *ie.IE PresenceReportingAreaAction []*ie.IE PGWNodeLoadControlInformation *ie.IE PGWAPNLoadControlInformation *ie.IE SGWNodeLoadControlInformation *ie.IE PGWOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE NBIFOMContainer *ie.IE PDNConnectionChargingID *ie.IE EPCO *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewCreateSessionResponse creates a new CreateSessionResponse. func NewCreateSessionResponse(teid, seq uint32, ies ...*ie.IE) *CreateSessionResponse { c := &CreateSessionResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeCreateSessionResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.ChangeReportingAction: c.ChangeReportingAction = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.HeNBInformationReporting: c.HeNBInformationReporting = i case ie.FullyQualifiedTEID: switch i.Instance() { case 0: c.SenderFTEIDC = i case 1: c.PGWS5S8FTEIDC = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PDNAddressAllocation: c.PAA = i case ie.APNRestriction: c.APNRestriction = i case ie.AggregateMaximumBitRate: c.AMBR = i case ie.EPSBearerID: c.EBI = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.BearerContext: switch i.Instance() { case 0: c.BearerContextsCreated = append(c.BearerContextsCreated, i) case 1: c.BearerContextMarkedForRemoval = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.Recovery: c.Recovery = i case ie.FullyQualifiedDomainName: c.ChargingGatewayName = i case ie.IPAddress: c.ChargingGatewayAddress = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.PGWFQCSID = i case 1: c.SGWFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.LocalDistinguishedName: switch i.Instance() { case 0: c.PGWLDN = i case 1: c.SGWLDN = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.EPCTimer: c.PGWBackOffTime = i case ie.AdditionalProtocolConfigurationOptions: c.APCO = i case ie.IPv4ConfigurationParameters: c.TrustedTWANIPv4Parameters = i case ie.Indication: c.IndicationFlags = i case ie.PresenceReportingAreaAction: c.PresenceReportingAreaAction = append(c.PresenceReportingAreaAction, i) case ie.LoadControlInformation: switch i.Instance() { case 0: c.PGWNodeLoadControlInformation = i case 1: c.PGWAPNLoadControlInformation = i case 2: c.SGWNodeLoadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.PGWOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FContainer: c.NBIFOMContainer = i case ie.ChargingID: c.PDNConnectionChargingID = i case ie.ExtendedProtocolConfigurationOptions: c.EPCO = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes CreateSessionResponse into bytes. func (c *CreateSessionResponse) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes CreateSessionResponse into bytes. func (c *CreateSessionResponse) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChangeReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.HeNBInformationReporting; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SenderFTEIDC; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWS5S8FTEIDC; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PAA; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APNRestriction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.AMBR; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EBI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.BearerContextsCreated { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.BearerContextMarkedForRemoval; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Recovery; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChargingGatewayName; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChargingGatewayAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWLDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWLDN; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWBackOffTime; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TrustedTWANIPv4Parameters; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaAction { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWAPNLoadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PDNConnectionChargingID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EPCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseCreateSessionResponse decodes given bytes as CreateSessionResponse. func ParseCreateSessionResponse(b []byte) (*CreateSessionResponse, error) { c := &CreateSessionResponse{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as CreateSessionResponse. func (c *CreateSessionResponse) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.ChangeReportingAction: c.ChangeReportingAction = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.HeNBInformationReporting: c.HeNBInformationReporting = i case ie.FullyQualifiedTEID: switch i.Instance() { case 0: c.SenderFTEIDC = i case 1: c.PGWS5S8FTEIDC = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PDNAddressAllocation: c.PAA = i case ie.APNRestriction: c.APNRestriction = i case ie.AggregateMaximumBitRate: c.AMBR = i case ie.EPSBearerID: c.EBI = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.BearerContext: switch i.Instance() { case 0: c.BearerContextsCreated = append(c.BearerContextsCreated, i) case 1: c.BearerContextMarkedForRemoval = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.Recovery: c.Recovery = i case ie.FullyQualifiedDomainName: c.ChargingGatewayName = i case ie.IPAddress: c.ChargingGatewayAddress = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.PGWFQCSID = i case 1: c.SGWFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.LocalDistinguishedName: switch i.Instance() { case 0: c.PGWLDN = i case 1: c.SGWLDN = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.EPCTimer: c.PGWBackOffTime = i case ie.AdditionalProtocolConfigurationOptions: c.APCO = i case ie.IPv4ConfigurationParameters: c.TrustedTWANIPv4Parameters = i case ie.Indication: c.IndicationFlags = i case ie.PresenceReportingAreaAction: c.PresenceReportingAreaAction = append(c.PresenceReportingAreaAction, i) case ie.LoadControlInformation: switch i.Instance() { case 0: c.PGWNodeLoadControlInformation = i case 1: c.PGWAPNLoadControlInformation = i case 2: c.SGWNodeLoadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.PGWOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FContainer: c.NBIFOMContainer = i case ie.ChargingID: c.PDNConnectionChargingID = i case ie.ExtendedProtocolConfigurationOptions: c.EPCO = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *CreateSessionResponse) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.Cause; ie != nil { l += ie.MarshalLen() } if ie := c.ChangeReportingAction; ie != nil { l += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { l += ie.MarshalLen() } if ie := c.HeNBInformationReporting; ie != nil { l += ie.MarshalLen() } if ie := c.SenderFTEIDC; ie != nil { l += ie.MarshalLen() } if ie := c.PGWS5S8FTEIDC; ie != nil { l += ie.MarshalLen() } if ie := c.PAA; ie != nil { l += ie.MarshalLen() } if ie := c.APNRestriction; ie != nil { l += ie.MarshalLen() } if ie := c.AMBR; ie != nil { l += ie.MarshalLen() } if ie := c.EBI; ie != nil { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } for _, ie := range c.BearerContextsCreated { l += ie.MarshalLen() } if ie := c.BearerContextMarkedForRemoval; ie != nil { l += ie.MarshalLen() } if ie := c.Recovery; ie != nil { l += ie.MarshalLen() } if ie := c.ChargingGatewayName; ie != nil { l += ie.MarshalLen() } if ie := c.ChargingGatewayAddress; ie != nil { l += ie.MarshalLen() } if ie := c.PGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.PGWLDN; ie != nil { l += ie.MarshalLen() } if ie := c.SGWLDN; ie != nil { l += ie.MarshalLen() } if ie := c.PGWBackOffTime; ie != nil { l += ie.MarshalLen() } if ie := c.APCO; ie != nil { l += ie.MarshalLen() } if ie := c.TrustedTWANIPv4Parameters; ie != nil { l += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { l += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaAction { l += ie.MarshalLen() } if ie := c.PGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.PGWAPNLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.SGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.PGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { l += ie.MarshalLen() } if ie := c.PDNConnectionChargingID; ie != nil { l += ie.MarshalLen() } if ie := c.EPCO; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *CreateSessionResponse) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *CreateSessionResponse) MessageTypeName() string { return "Create Session Response" } // TEID returns the TEID in uint32. func (c *CreateSessionResponse) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/create-session-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes CreateSessionResponse into bytes. // // Deprecated: use CreateSessionResponse.Marshal instead. func (c *CreateSessionResponse) Serialize() ([]byte, error) { log.Println("CreateSessionResponse.Serialize is deprecated. use CreateSessionResponse.Marshal instead") return c.Marshal() } // SerializeTo serializes CreateSessionResponse into bytes given as b. // // Deprecated: use CreateSessionResponse.MarshalTo instead. func (c *CreateSessionResponse) SerializeTo(b []byte) error { log.Println("CreateSessionResponse.SerializeTo is deprecated. use CreateSessionResponse.MarshalTo instead") return c.MarshalTo(b) } // DecodeCreateSessionResponse decodes bytes as CreateSessionResponse. // // Deprecated: use ParseCreateSessionResponse instead. func DecodeCreateSessionResponse(b []byte) (*CreateSessionResponse, error) { log.Println("DecodeCreateSessionResponse is deprecated. use ParseCreateSessionResponse instead") return ParseCreateSessionResponse(b) } // DecodeFromBytes decodes bytes as CreateSessionResponse. // // Deprecated: use CreateSessionResponse.UnmarshalBinary instead. func (c *CreateSessionResponse) DecodeFromBytes(b []byte) error { log.Println("CreateSessionResponse.DecodeFromBytes is deprecated. use CreateSessionResponse.UnmarshalBinary instead") return c.UnmarshalBinary(b) } // Len returns the actual length of CreateSessionResponse. // // Deprecated: use CreateSessionResponse.MarshalLen instead. func (c *CreateSessionResponse) Len() int { log.Println("CreateSessionResponse.Len is deprecated. use CreateSessionResponse.MarshalLen instead") return c.MarshalLen() } ================================================ FILE: gtpv2/message/create-session-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestCreateSessionResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/FromSGWtoMME", Structured: message.NewCreateSessionResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11S4SGWGTPC, 0xffffffff, "1.1.1.3", ""), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, 0xffffffff, "1.1.1.2", "").WithInstance(1), ie.NewPDNAddressAllocation("2.2.2.2"), ie.NewAPNRestriction(gtpv2.APNRestrictionPublic1), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1USGWGTPU, 0xffffffff, "1.1.1.3", ""), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPU, 0xffffffff, "1.1.1.2", "").WithInstance(1), ), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1USGWGTPU, 0xffffffff, "1.1.1.3", ""), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPU, 0xffffffff, "1.1.1.2", "").WithInstance(1), ), ie.NewFullyQualifiedCSID("1.1.1.2", 1), ie.NewFullyQualifiedCSID("1.1.1.3", 1).WithInstance(1), ie.NewChargingID(1), ), Serialized: []byte{ // Header 0x48, 0x21, 0x00, 0xa6, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // F-TEID 0x57, 0x00, 0x09, 0x00, 0x8b, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x03, // F-TEID 0x57, 0x00, 0x09, 0x01, 0x87, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x02, // PAA 0x4f, 0x00, 0x05, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, // APNRestriction 0x7f, 0x00, 0x01, 0x00, 0x01, // BearerContext 1 0x5d, 0x00, 0x25, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // F-TEID 0x57, 0x00, 0x09, 0x00, 0x81, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x03, // F-TEID 0x57, 0x00, 0x09, 0x01, 0x85, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x02, // BearerContext 2 0x5d, 0x00, 0x25, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // F-TEID 0x57, 0x00, 0x09, 0x00, 0x81, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x03, // F-TEID 0x57, 0x00, 0x09, 0x01, 0x85, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x02, // FQ-CSID 0x84, 0x00, 0x07, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x84, 0x00, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x00, 0x01, // ChargingID 0x5e, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseCreateSessionResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/delete-bearer-command.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // DeleteBearerCommand is a DeleteBearerCommand Header and its IEs above. type DeleteBearerCommand struct { *Header BearerContexts []*ie.IE ULI *ie.IE ULITimestamp *ie.IE UETimeZone *ie.IE MMESGSNOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE SenderFTEIDC *ie.IE SecondaryRATDataUsageReport []*ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeleteBearerCommand creates a new DeleteBearerCommand. func NewDeleteBearerCommand(teid, seq uint32, ies ...*ie.IE) *DeleteBearerCommand { d := &DeleteBearerCommand{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDeleteBearerCommand, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.BearerContext: d.BearerContexts = append(d.BearerContexts, i) case ie.UserLocationInformation: d.ULI = i case ie.ULITimestamp: d.ULITimestamp = i case ie.UETimeZone: d.UETimeZone = i case ie.OverloadControlInformation: switch i.Instance() { case 0: d.MMESGSNOverloadControlInformation = i case 1: d.SGWOverloadControlInformation = i } case ie.FullyQualifiedTEID: d.SenderFTEIDC = i case ie.SecondaryRATUsageDataReport: d.SecondaryRATDataUsageReport = append(d.SecondaryRATDataUsageReport, i) case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal serializes DeleteBearerCommand into bytes. func (d *DeleteBearerCommand) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DeleteBearerCommand into bytes. func (d *DeleteBearerCommand) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 for _, ie := range d.BearerContexts { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.UETimeZone; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.MMESGSNOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SenderFTEIDC; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.SecondaryRATDataUsageReport { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeleteBearerCommand decodes given bytes as DeleteBearerCommand. func ParseDeleteBearerCommand(b []byte) (*DeleteBearerCommand, error) { d := &DeleteBearerCommand{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes given bytes as DeleteBearerCommand. func (d *DeleteBearerCommand) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.BearerContext: d.BearerContexts = append(d.BearerContexts, i) case ie.UserLocationInformation: d.ULI = i case ie.ULITimestamp: d.ULITimestamp = i case ie.UETimeZone: d.UETimeZone = i case ie.OverloadControlInformation: switch i.Instance() { case 0: d.MMESGSNOverloadControlInformation = i case 1: d.SGWOverloadControlInformation = i } case ie.FullyQualifiedTEID: d.SenderFTEIDC = i case ie.SecondaryRATUsageDataReport: d.SecondaryRATDataUsageReport = append(d.SecondaryRATDataUsageReport, i) case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (d *DeleteBearerCommand) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) for _, ie := range d.BearerContexts { l += ie.MarshalLen() } if ie := d.ULI; ie != nil { l += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { l += ie.MarshalLen() } if ie := d.UETimeZone; ie != nil { l += ie.MarshalLen() } if ie := d.MMESGSNOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SenderFTEIDC; ie != nil { l += ie.MarshalLen() } for _, ie := range d.SecondaryRATDataUsageReport { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeleteBearerCommand) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (d *DeleteBearerCommand) MessageTypeName() string { return "Delete Bearer Command" } // TEID returns the TEID in uint32. func (d *DeleteBearerCommand) TEID() uint32 { return d.Header.teid() } ================================================ FILE: gtpv2/message/delete-bearer-command_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "time" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDeleteBearerCommand(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDeleteBearerCommand( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewBearerContext(ie.NewEPSBearerID(5), ie.NewDelayValue(500*time.Millisecond), ie.NewDelayValue(100*time.Millisecond)), ie.NewBearerContext(ie.NewEPSBearerID(6), ie.NewDelayValue(500*time.Millisecond), ie.NewDelayValue(100*time.Millisecond)), ), Serialized: []byte{ // Header 0x48, 0x42, 0x00, 0x2e, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // BearerContexts 1 0x5d, 0x00, 0x0f, 0x00, 0x49, 0x00, 0x01, 0x00, 0x05, 0x5c, 0x00, 0x01, 0x00, 0x0a, 0x5c, 0x00, 0x01, 0x00, 0x02, // BearerContexts 2 0x5d, 0x00, 0x0f, 0x00, 0x49, 0x00, 0x01, 0x00, 0x06, 0x5c, 0x00, 0x01, 0x00, 0x0a, 0x5c, 0x00, 0x01, 0x00, 0x02, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeleteBearerCommand(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/delete-bearer-failure-indication.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // DeleteBearerFailureIndication is a DeleteBearerFailureIndication Header and its IEs above. type DeleteBearerFailureIndication struct { *Header Cause *ie.IE BearerContexts []*ie.IE Recovery *ie.IE IndicationFlags *ie.IE PGWOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeleteBearerFailureIndication creates a new DeleteBearerFailureIndication. func NewDeleteBearerFailureIndication(teid, seq uint32, ies ...*ie.IE) *DeleteBearerFailureIndication { d := &DeleteBearerFailureIndication{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDeleteBearerFailureIndication, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.BearerContext: d.BearerContexts = append(d.BearerContexts, i) case ie.Recovery: d.Recovery = i case ie.Indication: d.IndicationFlags = i case ie.OverloadControlInformation: switch i.Instance() { case 0: d.PGWOverloadControlInformation = i case 1: d.SGWOverloadControlInformation = i } case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal serializes DeleteBearerFailureIndication into bytes. func (d *DeleteBearerFailureIndication) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DeleteBearerFailureIndication into bytes. func (d *DeleteBearerFailureIndication) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.BearerContexts { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.Recovery; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeleteBearerFailureIndication decodes given bytes as DeleteBearerFailureIndication. func ParseDeleteBearerFailureIndication(b []byte) (*DeleteBearerFailureIndication, error) { d := &DeleteBearerFailureIndication{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes given bytes as DeleteBearerFailureIndication. func (d *DeleteBearerFailureIndication) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.BearerContext: d.BearerContexts = append(d.BearerContexts, i) case ie.Recovery: d.Recovery = i case ie.Indication: d.IndicationFlags = i case ie.OverloadControlInformation: switch i.Instance() { case 0: d.PGWOverloadControlInformation = i case 1: d.SGWOverloadControlInformation = i } case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (d *DeleteBearerFailureIndication) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } for _, ie := range d.BearerContexts { l += ie.MarshalLen() } if ie := d.Recovery; ie != nil { l += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := d.PGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeleteBearerFailureIndication) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (d *DeleteBearerFailureIndication) MessageTypeName() string { return "Delete Bearer Failure Indication" } // TEID returns the TEID in uint32. func (d *DeleteBearerFailureIndication) TEID() uint32 { return d.Header.teid() } ================================================ FILE: gtpv2/message/delete-bearer-failure-indication_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDeleteBearerFailureIndication(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDeleteBearerFailureIndication( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewBearerContext(ie.NewEPSBearerID(5), ie.NewDelayValue(500*time.Millisecond), ie.NewDelayValue(100*time.Millisecond)), ie.NewBearerContext(ie.NewEPSBearerID(6), ie.NewDelayValue(500*time.Millisecond), ie.NewDelayValue(100*time.Millisecond)), ), Serialized: []byte{ // Header 0x48, 0x43, 0x00, 0x34, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // BearerContexts 1 0x5d, 0x00, 0x0f, 0x00, 0x49, 0x00, 0x01, 0x00, 0x05, 0x5c, 0x00, 0x01, 0x00, 0x0a, 0x5c, 0x00, 0x01, 0x00, 0x02, // BearerContexts 2 0x5d, 0x00, 0x0f, 0x00, 0x49, 0x00, 0x01, 0x00, 0x06, 0x5c, 0x00, 0x01, 0x00, 0x0a, 0x5c, 0x00, 0x01, 0x00, 0x02, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeleteBearerFailureIndication(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/delete-bearer-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DeleteBearerRequest is a DeleteBearerRequest Header and its IEs above. type DeleteBearerRequest struct { *Header LinkedEBI *ie.IE EBIs []*ie.IE FailedBearerContext *ie.IE PTI *ie.IE PCO *ie.IE PGWFQCSID *ie.IE SGWFQCSID *ie.IE Cause *ie.IE IndicationFlags *ie.IE PGWNodeLoadControlInformation *ie.IE PGWAPNLoadControlInformation *ie.IE SGWNodeLoadControlInformation *ie.IE PGWOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE NBIFOMContainer *ie.IE APNRateControlStatus *ie.IE EPCO *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeleteBearerRequest creates a new DeleteBearerRequest. func NewDeleteBearerRequest(teid, seq uint32, ies ...*ie.IE) *DeleteBearerRequest { d := &DeleteBearerRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDeleteBearerRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.EPSBearerID: switch i.Instance() { case 0: d.LinkedEBI = i case 1: d.EBIs = append(d.EBIs, i) default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.BearerContext: d.FailedBearerContext = i case ie.ProcedureTransactionID: d.PTI = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: d.PGWFQCSID = i case 1: d.SGWFQCSID = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.Cause: d.Cause = i case ie.Indication: d.IndicationFlags = i case ie.LoadControlInformation: switch i.Instance() { case 0: d.PGWNodeLoadControlInformation = i case 1: d.PGWAPNLoadControlInformation = i case 2: d.SGWNodeLoadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: d.PGWOverloadControlInformation = i case 1: d.SGWOverloadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.FContainer: d.NBIFOMContainer = i case ie.APNRateControlStatus: d.APNRateControlStatus = i case ie.ExtendedProtocolConfigurationOptions: d.EPCO = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal serializes DeleteBearerRequest into bytes. func (d *DeleteBearerRequest) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DeleteBearerRequest into bytes. func (d *DeleteBearerRequest) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.LinkedEBI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.EBIs { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.FailedBearerContext; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PTI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PCO; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PGWFQCSID; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWFQCSID; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PGWAPNLoadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.NBIFOMContainer; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.APNRateControlStatus; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.EPCO; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeleteBearerRequest decodes given bytes as DeleteBearerRequest. func ParseDeleteBearerRequest(b []byte) (*DeleteBearerRequest, error) { d := &DeleteBearerRequest{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes given bytes as DeleteBearerRequest. func (d *DeleteBearerRequest) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.EPSBearerID: switch i.Instance() { case 0: d.LinkedEBI = i case 1: d.EBIs = append(d.EBIs, i) default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.BearerContext: d.FailedBearerContext = i case ie.ProcedureTransactionID: d.PTI = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: d.PGWFQCSID = i case 1: d.SGWFQCSID = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.Cause: d.Cause = i case ie.Indication: d.IndicationFlags = i case ie.LoadControlInformation: switch i.Instance() { case 0: d.PGWNodeLoadControlInformation = i case 1: d.PGWAPNLoadControlInformation = i case 2: d.SGWNodeLoadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: d.PGWOverloadControlInformation = i case 1: d.SGWOverloadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.FContainer: d.NBIFOMContainer = i case ie.APNRateControlStatus: d.APNRateControlStatus = i case ie.ExtendedProtocolConfigurationOptions: d.EPCO = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (d *DeleteBearerRequest) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.LinkedEBI; ie != nil { l += ie.MarshalLen() } for _, ie := range d.EBIs { l += ie.MarshalLen() } if ie := d.FailedBearerContext; ie != nil { l += ie.MarshalLen() } if ie := d.PTI; ie != nil { l += ie.MarshalLen() } if ie := d.PCO; ie != nil { l += ie.MarshalLen() } if ie := d.PGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := d.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := d.PGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.PGWAPNLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.PGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.NBIFOMContainer; ie != nil { l += ie.MarshalLen() } if ie := d.APNRateControlStatus; ie != nil { l += ie.MarshalLen() } if ie := d.EPCO; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeleteBearerRequest) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (d *DeleteBearerRequest) MessageTypeName() string { return "Delete Bearer Request" } // TEID returns the TEID in uint32. func (d *DeleteBearerRequest) TEID() uint32 { return d.Header.teid() } ================================================ FILE: gtpv2/message/delete-bearer-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes DeleteBearerRequest into bytes. // // Deprecated: use DeleteBearerRequest.Marshal instead. func (d *DeleteBearerRequest) Serialize() ([]byte, error) { log.Println("DeleteBearerRequest.Serialize is deprecated. use DeleteBearerRequest.Marshal instead") return d.Marshal() } // SerializeTo serializes DeleteBearerRequest into bytes given as b. // // Deprecated: use DeleteBearerRequest.MarshalTo instead. func (d *DeleteBearerRequest) SerializeTo(b []byte) error { log.Println("DeleteBearerRequest.SerializeTo is deprecated. use DeleteBearerRequest.MarshalTo instead") return d.MarshalTo(b) } // DecodeDeleteBearerRequest decodes bytes as DeleteBearerRequest. // // Deprecated: use ParseDeleteBearerRequest instead. func DecodeDeleteBearerRequest(b []byte) (*DeleteBearerRequest, error) { log.Println("DecodeDeleteBearerRequest is deprecated. use ParseDeleteBearerRequest instead") return ParseDeleteBearerRequest(b) } // DecodeFromBytes decodes bytes as DeleteBearerRequest. // // Deprecated: use DeleteBearerRequest.UnmarshalBinary instead. func (d *DeleteBearerRequest) DecodeFromBytes(b []byte) error { log.Println("DeleteBearerRequest.DecodeFromBytes is deprecated. use DeleteBearerRequest.UnmarshalBinary instead") return d.UnmarshalBinary(b) } // Len returns the actual length of DeleteBearerRequest. // // Deprecated: use DeleteBearerRequest.MarshalLen instead. func (d *DeleteBearerRequest) Len() int { log.Println("DeleteBearerRequest.Len is deprecated. use DeleteBearerRequest.MarshalLen instead") return d.MarshalLen() } ================================================ FILE: gtpv2/message/delete-bearer-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDeleteBearerRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDeleteBearerRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewEPSBearerID(5), ie.NewCause(gtpv2.CauseISRDeactivation, 0, 0, 0, nil), ), Serialized: []byte{ // Header 0x48, 0x63, 0x00, 0x13, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // Cause 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, }, }, { Description: "EBIs", Structured: message.NewDeleteBearerRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewEPSBearerID(5).WithInstance(1), ie.NewEPSBearerID(6).WithInstance(1), ie.NewCause(gtpv2.CauseISRDeactivation, 0, 0, 0, nil), ), Serialized: []byte{ // Header 0x48, 0x63, 0x00, 0x18, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // EBIs 1 0x49, 0x00, 0x01, 0x01, 0x05, // EBIs 2 0x49, 0x00, 0x01, 0x01, 0x06, // Cause 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeleteBearerRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/delete-bearer-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DeleteBearerResponse is a DeleteBearerResponse Header and its IEs above. type DeleteBearerResponse struct { *Header Cause *ie.IE LinkedEBI *ie.IE BearerContexts []*ie.IE Recovery *ie.IE MMEFQCSID *ie.IE SGWFQCSID *ie.IE EPDGFQCSID *ie.IE TWANFQCSID *ie.IE PCO *ie.IE UETimeZone *ie.IE ULI *ie.IE ULITimestamp *ie.IE TWANIdentifier *ie.IE TWANIdentifierTimestamp *ie.IE MMEOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE MMESGSNIdentifier *ie.IE TWANePDGOverloadControlInformation *ie.IE WLANLocationInformation *ie.IE WLANLocationTimestamp *ie.IE UELocalIPAddress *ie.IE UEUDPPort *ie.IE NBIFOMContainer *ie.IE UETCPPort *ie.IE SecondaryRATUsageDataReport []*ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeleteBearerResponse creates a new DeleteBearerResponse. func NewDeleteBearerResponse(teid, seq uint32, ies ...*ie.IE) *DeleteBearerResponse { d := &DeleteBearerResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDeleteBearerResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.EPSBearerID: d.LinkedEBI = i case ie.BearerContext: d.BearerContexts = append(d.BearerContexts, i) case ie.Recovery: d.Recovery = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: d.MMEFQCSID = i case 1: d.SGWFQCSID = i case 2: d.EPDGFQCSID = i case 3: d.TWANFQCSID = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.ProtocolConfigurationOptions: d.PCO = i case ie.UETimeZone: d.UETimeZone = i case ie.UserLocationInformation: d.ULI = i case ie.ULITimestamp: d.ULITimestamp = i case ie.TWANIdentifier: switch i.Instance() { case 0: d.TWANIdentifier = i case 1: d.WLANLocationInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.TWANIdentifierTimestamp: switch i.Instance() { case 0: d.TWANIdentifierTimestamp = i case 1: d.WLANLocationTimestamp = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: d.MMEOverloadControlInformation = i case 1: d.SGWOverloadControlInformation = i case 2: d.TWANePDGOverloadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.IPAddress: switch i.Instance() { case 0: d.MMESGSNIdentifier = i case 1: d.UELocalIPAddress = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.PortNumber: switch i.Instance() { case 0: d.UEUDPPort = i case 1: d.UETCPPort = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.FContainer: d.NBIFOMContainer = i case ie.SecondaryRATUsageDataReport: d.SecondaryRATUsageDataReport = append(d.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal serializes DeleteBearerResponse into bytes. func (d *DeleteBearerResponse) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DeleteBearerResponse into bytes. func (d *DeleteBearerResponse) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.LinkedEBI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.BearerContexts { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.Recovery; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.MMEFQCSID; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWFQCSID; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.EPDGFQCSID; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.TWANFQCSID; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PCO; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.UETimeZone; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.TWANIdentifier; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.TWANIdentifierTimestamp; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.MMEOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.MMESGSNIdentifier; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.TWANePDGOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.WLANLocationInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.WLANLocationTimestamp; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.UELocalIPAddress; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.UEUDPPort; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.NBIFOMContainer; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.UETCPPort; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.SecondaryRATUsageDataReport { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeleteBearerResponse decodes given bytes as DeleteBearerResponse. func ParseDeleteBearerResponse(b []byte) (*DeleteBearerResponse, error) { d := &DeleteBearerResponse{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes given bytes as DeleteBearerResponse. func (d *DeleteBearerResponse) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.EPSBearerID: d.LinkedEBI = i case ie.BearerContext: d.BearerContexts = append(d.BearerContexts, i) case ie.Recovery: d.Recovery = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: d.MMEFQCSID = i case 1: d.SGWFQCSID = i case 2: d.EPDGFQCSID = i case 3: d.TWANFQCSID = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.ProtocolConfigurationOptions: d.PCO = i case ie.UETimeZone: d.UETimeZone = i case ie.UserLocationInformation: d.ULI = i case ie.ULITimestamp: d.ULITimestamp = i case ie.TWANIdentifier: switch i.Instance() { case 0: d.TWANIdentifier = i case 1: d.WLANLocationInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.TWANIdentifierTimestamp: switch i.Instance() { case 0: d.TWANIdentifierTimestamp = i case 1: d.WLANLocationTimestamp = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: d.MMEOverloadControlInformation = i case 1: d.SGWOverloadControlInformation = i case 2: d.TWANePDGOverloadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.IPAddress: switch i.Instance() { case 0: d.MMESGSNIdentifier = i case 1: d.UELocalIPAddress = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.PortNumber: switch i.Instance() { case 0: d.UEUDPPort = i case 1: d.UETCPPort = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.FContainer: d.NBIFOMContainer = i case ie.SecondaryRATUsageDataReport: d.SecondaryRATUsageDataReport = append(d.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (d *DeleteBearerResponse) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.LinkedEBI; ie != nil { l += ie.MarshalLen() } for _, ie := range d.BearerContexts { l += ie.MarshalLen() } if ie := d.Recovery; ie != nil { l += ie.MarshalLen() } if ie := d.MMEFQCSID; ie != nil { l += ie.MarshalLen() } if ie := d.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := d.EPDGFQCSID; ie != nil { l += ie.MarshalLen() } if ie := d.TWANFQCSID; ie != nil { l += ie.MarshalLen() } if ie := d.PCO; ie != nil { l += ie.MarshalLen() } if ie := d.UETimeZone; ie != nil { l += ie.MarshalLen() } if ie := d.ULI; ie != nil { l += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { l += ie.MarshalLen() } if ie := d.TWANIdentifier; ie != nil { l += ie.MarshalLen() } if ie := d.TWANIdentifierTimestamp; ie != nil { l += ie.MarshalLen() } if ie := d.MMEOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.MMESGSNIdentifier; ie != nil { l += ie.MarshalLen() } if ie := d.TWANePDGOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.WLANLocationInformation; ie != nil { l += ie.MarshalLen() } if ie := d.WLANLocationTimestamp; ie != nil { l += ie.MarshalLen() } if ie := d.UELocalIPAddress; ie != nil { l += ie.MarshalLen() } if ie := d.UEUDPPort; ie != nil { l += ie.MarshalLen() } if ie := d.NBIFOMContainer; ie != nil { l += ie.MarshalLen() } if ie := d.UETCPPort; ie != nil { l += ie.MarshalLen() } for _, ie := range d.SecondaryRATUsageDataReport { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeleteBearerResponse) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (d *DeleteBearerResponse) MessageTypeName() string { return "Delete Bearer Response" } // TEID returns the TEID in uint32. func (d *DeleteBearerResponse) TEID() uint32 { return d.Header.teid() } ================================================ FILE: gtpv2/message/delete-bearer-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes DeleteBearerResponse into bytes. // // Deprecated: use DeleteBearerResponse.Marshal instead. func (d *DeleteBearerResponse) Serialize() ([]byte, error) { log.Println("DeleteBearerResponse.Serialize is deprecated. use DeleteBearerResponse.Marshal instead") return d.Marshal() } // SerializeTo serializes DeleteBearerResponse into bytes given as b. // // Deprecated: use DeleteBearerResponse.MarshalTo instead. func (d *DeleteBearerResponse) SerializeTo(b []byte) error { log.Println("DeleteBearerResponse.SerializeTo is deprecated. use DeleteBearerResponse.MarshalTo instead") return d.MarshalTo(b) } // DecodeDeleteBearerResponse decodes bytes as DeleteBearerResponse. // // Deprecated: use ParseDeleteBearerResponse instead. func DecodeDeleteBearerResponse(b []byte) (*DeleteBearerResponse, error) { log.Println("DecodeDeleteBearerResponse is deprecated. use ParseDeleteBearerResponse instead") return ParseDeleteBearerResponse(b) } // DecodeFromBytes decodes bytes as DeleteBearerResponse. // // Deprecated: use DeleteBearerResponse.UnmarshalBinary instead. func (d *DeleteBearerResponse) DecodeFromBytes(b []byte) error { log.Println("DeleteBearerResponse.DecodeFromBytes is deprecated. use DeleteBearerResponse.UnmarshalBinary instead") return d.UnmarshalBinary(b) } // Len returns the actual length of DeleteBearerResponse. // // Deprecated: use DeleteBearerResponse.MarshalLen instead. func (d *DeleteBearerResponse) Len() int { log.Println("DeleteBearerResponse.Len is deprecated. use DeleteBearerResponse.MarshalLen instead") return d.MarshalLen() } ================================================ FILE: gtpv2/message/delete-bearer-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDeleteBearerResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/CauseOnly", Structured: message.NewDeleteBearerResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewBearerContext( ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ie.NewBearerContext( ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ), Serialized: []byte{ // Header 0x48, 0x64, 0x00, 0x3a, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // BearerContext 1 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, // BearerContext 2 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeleteBearerResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/delete-pdn-connection-set-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DeletePDNConnectionSetRequest is a DeletePDNConnectionSetRequest Header and its IEs above. type DeletePDNConnectionSetRequest struct { *Header MMEFQCSID *ie.IE SGWFQCSID *ie.IE PGWFQCSID *ie.IE EPDGFQCSID *ie.IE TWANFQCSID *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeletePDNConnectionSetRequest creates a new DeletePDNConnectionSetRequest. func NewDeletePDNConnectionSetRequest(teid, seq uint32, ies ...*ie.IE) *DeletePDNConnectionSetRequest { m := &DeletePDNConnectionSetRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDeletePDNConnectionSetRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.FullyQualifiedCSID: switch i.Instance() { case 0: m.MMEFQCSID = i case 1: m.SGWFQCSID = i case 2: m.PGWFQCSID = i case 3: m.EPDGFQCSID = i case 4: m.TWANFQCSID = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes DeletePDNConnectionSetRequest into bytes. func (m *DeletePDNConnectionSetRequest) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DeletePDNConnectionSetRequest into bytes. func (m *DeletePDNConnectionSetRequest) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.MMEFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PGWFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.EPDGFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.TWANFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseDeletePDNConnectionSetRequest decodes given bytes as DeletePDNConnectionSetRequest. func ParseDeletePDNConnectionSetRequest(b []byte) (*DeletePDNConnectionSetRequest, error) { m := &DeletePDNConnectionSetRequest{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as DeletePDNConnectionSetRequest. func (m *DeletePDNConnectionSetRequest) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.FullyQualifiedCSID: switch i.Instance() { case 0: m.MMEFQCSID = i case 1: m.SGWFQCSID = i case 2: m.PGWFQCSID = i case 3: m.EPDGFQCSID = i case 4: m.TWANFQCSID = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *DeletePDNConnectionSetRequest) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.MMEFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.PGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.EPDGFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.TWANFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *DeletePDNConnectionSetRequest) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *DeletePDNConnectionSetRequest) MessageTypeName() string { return "Delete PDN Connection Set Request" } // TEID returns the TEID in uint32. func (m *DeletePDNConnectionSetRequest) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/delete-pdn-connection-set-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDeletePDNConnectionSetRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDeletePDNConnectionSetRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewFullyQualifiedCSID("1.1.1.1", 1), ie.NewFullyQualifiedCSID("1.1.1.1", 1).WithInstance(4), ), Serialized: []byte{ // Header 0x48, 0x65, 0x00, 0x1e, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // MME-FQ-CSID 0x84, 0x00, 0x07, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, // TWAN-FQ-CSID 0x84, 0x00, 0x07, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeletePDNConnectionSetRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/delete-pdn-connection-set-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DeletePDNConnectionSetResponse is a DeletePDNConnectionSetResponse Header and its IEs above. type DeletePDNConnectionSetResponse struct { *Header Cause *ie.IE Recovery *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeletePDNConnectionSetResponse creates a new DeletePDNConnectionSetResponse. func NewDeletePDNConnectionSetResponse(teid, seq uint32, ies ...*ie.IE) *DeletePDNConnectionSetResponse { m := &DeletePDNConnectionSetResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDeletePDNConnectionSetResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.Recovery: m.Recovery = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal returns the byte sequence generated from a DeletePDNConnectionSetResponse. func (m *DeletePDNConnectionSetResponse) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (m *DeletePDNConnectionSetResponse) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.Cause; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.Recovery; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseDeletePDNConnectionSetResponse decodes a given byte sequence as a DeletePDNConnectionSetResponse. func ParseDeletePDNConnectionSetResponse(b []byte) (*DeletePDNConnectionSetResponse, error) { m := &DeletePDNConnectionSetResponse{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given byte sequence as DeletePDNConnectionSetResponse. func (m *DeletePDNConnectionSetResponse) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.Recovery: m.Recovery = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (m *DeletePDNConnectionSetResponse) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.Cause; ie != nil { l += ie.MarshalLen() } if ie := m.Recovery; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *DeletePDNConnectionSetResponse) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *DeletePDNConnectionSetResponse) MessageTypeName() string { return "Delete PDN Connection Set Response" } // TEID returns the TEID in uint32. func (m *DeletePDNConnectionSetResponse) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/delete-pdn-connection-set-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDeletePDNConnectionSetResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDeletePDNConnectionSetResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewRecovery(0xff), ), Serialized: []byte{ // Header 0x48, 0x66, 0x00, 0x13, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // Recovery 0x03, 0x00, 0x01, 0x00, 0xff, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeletePDNConnectionSetResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/delete-session-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DeleteSessionRequest is a DeleteSessionRequest Header and its IEs above. type DeleteSessionRequest struct { *Header Cause *ie.IE LinkedEBI *ie.IE ULI *ie.IE IndicationFlags *ie.IE PCO *ie.IE OriginatingNode *ie.IE SenderFTEIDC *ie.IE UETimeZone *ie.IE ULITimestamp *ie.IE RANNASReleaseCause *ie.IE TWANIdentifier *ie.IE TWANIdentifierTimestamp *ie.IE MMESGSNOverloadControlInformation *ie.IE SGWOverloadControlInformaion *ie.IE TWANePDGOverloadControlInformaion *ie.IE WLANLocationInformation *ie.IE WLANLocationTimeStamp *ie.IE UELocalIPAddress *ie.IE UEUDPPort *ie.IE EPCO *ie.IE UETCPPort *ie.IE SecondaryRATUsageDataReport *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeleteSessionRequest creates a new DeleteSessionRequest. func NewDeleteSessionRequest(teid, seq uint32, ies ...*ie.IE) *DeleteSessionRequest { d := &DeleteSessionRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDeleteSessionRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.EPSBearerID: d.LinkedEBI = i case ie.UserLocationInformation: d.ULI = i case ie.Indication: d.IndicationFlags = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.NodeType: d.OriginatingNode = i case ie.FullyQualifiedTEID: d.SenderFTEIDC = i case ie.UETimeZone: d.UETimeZone = i case ie.ULITimestamp: d.ULITimestamp = i case ie.RANNASCause: d.RANNASReleaseCause = i case ie.TWANIdentifier: switch i.Instance() { case 0: d.TWANIdentifier = i case 1: d.WLANLocationInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.TWANIdentifierTimestamp: switch i.Instance() { case 0: d.TWANIdentifierTimestamp = i case 1: d.WLANLocationTimeStamp = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: d.MMESGSNOverloadControlInformation = i case 1: d.SGWOverloadControlInformaion = i case 2: d.TWANePDGOverloadControlInformaion = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.IPAddress: d.UELocalIPAddress = i case ie.PortNumber: switch i.Instance() { case 0: d.UEUDPPort = i case 1: d.UETCPPort = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.ExtendedProtocolConfigurationOptions: d.EPCO = i case ie.SecondaryRATUsageDataReport: d.SecondaryRATUsageDataReport = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal serializes DeleteSessionRequest into bytes. func (d *DeleteSessionRequest) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DeleteSessionRequest into bytes. func (d *DeleteSessionRequest) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.LinkedEBI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PCO; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.OriginatingNode; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SenderFTEIDC; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.UETimeZone; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.RANNASReleaseCause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.TWANIdentifier; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.TWANIdentifierTimestamp; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.MMESGSNOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWOverloadControlInformaion; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.TWANePDGOverloadControlInformaion; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.WLANLocationInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.WLANLocationTimeStamp; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.UELocalIPAddress; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.UEUDPPort; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.EPCO; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.UETCPPort; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SecondaryRATUsageDataReport; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeleteSessionRequest decodes given bytes as DeleteSessionRequest. func ParseDeleteSessionRequest(b []byte) (*DeleteSessionRequest, error) { d := &DeleteSessionRequest{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes given bytes as DeleteSessionRequest. func (d *DeleteSessionRequest) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.EPSBearerID: d.LinkedEBI = i case ie.UserLocationInformation: d.ULI = i case ie.Indication: d.IndicationFlags = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.NodeType: d.OriginatingNode = i case ie.FullyQualifiedTEID: d.SenderFTEIDC = i case ie.UETimeZone: d.UETimeZone = i case ie.ULITimestamp: d.ULITimestamp = i case ie.RANNASCause: d.RANNASReleaseCause = i case ie.TWANIdentifier: switch i.Instance() { case 0: d.TWANIdentifier = i case 1: d.WLANLocationInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.TWANIdentifierTimestamp: switch i.Instance() { case 0: d.TWANIdentifierTimestamp = i case 1: d.WLANLocationTimeStamp = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: d.MMESGSNOverloadControlInformation = i case 1: d.SGWOverloadControlInformaion = i case 2: d.TWANePDGOverloadControlInformaion = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.IPAddress: d.UELocalIPAddress = i case ie.PortNumber: switch i.Instance() { case 0: d.UEUDPPort = i case 1: d.UETCPPort = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.ExtendedProtocolConfigurationOptions: d.EPCO = i case ie.SecondaryRATUsageDataReport: d.SecondaryRATUsageDataReport = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (d *DeleteSessionRequest) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.LinkedEBI; ie != nil { l += ie.MarshalLen() } if ie := d.ULI; ie != nil { l += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := d.PCO; ie != nil { l += ie.MarshalLen() } if ie := d.OriginatingNode; ie != nil { l += ie.MarshalLen() } if ie := d.SenderFTEIDC; ie != nil { l += ie.MarshalLen() } if ie := d.UETimeZone; ie != nil { l += ie.MarshalLen() } if ie := d.ULITimestamp; ie != nil { l += ie.MarshalLen() } if ie := d.RANNASReleaseCause; ie != nil { l += ie.MarshalLen() } if ie := d.TWANIdentifier; ie != nil { l += ie.MarshalLen() } if ie := d.TWANIdentifierTimestamp; ie != nil { l += ie.MarshalLen() } if ie := d.MMESGSNOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SGWOverloadControlInformaion; ie != nil { l += ie.MarshalLen() } if ie := d.TWANePDGOverloadControlInformaion; ie != nil { l += ie.MarshalLen() } if ie := d.WLANLocationInformation; ie != nil { l += ie.MarshalLen() } if ie := d.WLANLocationTimeStamp; ie != nil { l += ie.MarshalLen() } if ie := d.UELocalIPAddress; ie != nil { l += ie.MarshalLen() } if ie := d.UEUDPPort; ie != nil { l += ie.MarshalLen() } if ie := d.EPCO; ie != nil { l += ie.MarshalLen() } if ie := d.UETCPPort; ie != nil { l += ie.MarshalLen() } if ie := d.SecondaryRATUsageDataReport; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeleteSessionRequest) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (d *DeleteSessionRequest) MessageTypeName() string { return "Delete Session Request" } // TEID returns the TEID in uint32. func (d *DeleteSessionRequest) TEID() uint32 { return d.Header.teid() } ================================================ FILE: gtpv2/message/delete-session-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes DeleteSessionRequest into bytes. // // Deprecated: use DeleteSessionRequest.Marshal instead. func (d *DeleteSessionRequest) Serialize() ([]byte, error) { log.Println("DeleteSessionRequest.Serialize is deprecated. use DeleteSessionRequest.Marshal instead") return d.Marshal() } // SerializeTo serializes DeleteSessionRequest into bytes given as b. // // Deprecated: use DeleteSessionRequest.MarshalTo instead. func (d *DeleteSessionRequest) SerializeTo(b []byte) error { log.Println("DeleteSessionRequest.SerializeTo is deprecated. use DeleteSessionRequest.MarshalTo instead") return d.MarshalTo(b) } // DecodeDeleteSessionRequest decodes bytes as DeleteSessionRequest. // // Deprecated: use ParseDeleteSessionRequest instead. func DecodeDeleteSessionRequest(b []byte) (*DeleteSessionRequest, error) { log.Println("DecodeDeleteSessionRequest is deprecated. use ParseDeleteSessionRequest instead") return ParseDeleteSessionRequest(b) } // DecodeFromBytes decodes bytes as DeleteSessionRequest. // // Deprecated: use DeleteSessionRequest.UnmarshalBinary instead. func (d *DeleteSessionRequest) DecodeFromBytes(b []byte) error { log.Println("DeleteSessionRequest.DecodeFromBytes is deprecated. use DeleteSessionRequest.UnmarshalBinary instead") return d.UnmarshalBinary(b) } // Len returns the actual length of DeleteSessionRequest. // // Deprecated: use DeleteSessionRequest.MarshalLen instead. func (d *DeleteSessionRequest) Len() int { log.Println("DeleteSessionRequest.Len is deprecated. use DeleteSessionRequest.MarshalLen instead") return d.MarshalLen() } ================================================ FILE: gtpv2/message/delete-session-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "time" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDeleteSessionRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/FromMMEtoSGW", Structured: message.NewDeleteSessionRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewEPSBearerID(5), ie.NewUserLocationInformationStruct( nil, nil, nil, ie.NewTAI("123", "45", 0x0001), ie.NewECGI("123", "45", 0x00000101), nil, nil, nil, ), ie.NewIndicationFromOctets(0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40), ie.NewULITimestamp(time.Date(2019, time.January, 1, 0, 0, 0, 0, time.UTC)), ), Serialized: []byte{ // Header 0x48, 0x24, 0x00, 0x31, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // ULI: TAI ECGI 0x56, 0x00, 0x0d, 0x00, 0x18, 0x21, 0xf3, 0x54, 0x00, 0x01, 0x21, 0xf3, 0x54, 0x00, 0x00, 0x01, 0x01, // Indication 0x4d, 0x00, 0x07, 0x00, 0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40, // ULITimestamp 0xaa, 0x00, 0x04, 0x00, 0xdf, 0xd5, 0x2c, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeleteSessionRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/delete-session-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DeleteSessionResponse is a DeleteSessionResponse Header and its IEs above. type DeleteSessionResponse struct { *Header Cause *ie.IE Recovery *ie.IE PCO *ie.IE IndicationFlags *ie.IE PGWNodeLoadControlInformation *ie.IE PGWAPNLoadControlInformation *ie.IE SGWNodeLoadControlInformation *ie.IE PGWOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE EPCO *ie.IE APNRateControlStatus *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDeleteSessionResponse creates a new DeleteSessionResponse. func NewDeleteSessionResponse(teid, seq uint32, ies ...*ie.IE) *DeleteSessionResponse { d := &DeleteSessionResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDeleteSessionResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.Recovery: d.Recovery = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.Indication: d.IndicationFlags = i case ie.LoadControlInformation: switch i.Instance() { case 1: d.PGWNodeLoadControlInformation = i case 2: d.PGWAPNLoadControlInformation = i case 3: d.SGWNodeLoadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 1: d.PGWOverloadControlInformation = i case 2: d.SGWOverloadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.ExtendedProtocolConfigurationOptions: d.EPCO = i case ie.APNRateControlStatus: d.APNRateControlStatus = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal serializes DeleteSessionResponse into bytes. func (d *DeleteSessionResponse) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DeleteSessionResponse into bytes. func (d *DeleteSessionResponse) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.Recovery; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PCO; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PGWAPNLoadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.EPCO; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.APNRateControlStatus; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDeleteSessionResponse decodes given bytes as DeleteSessionResponse. func ParseDeleteSessionResponse(b []byte) (*DeleteSessionResponse, error) { d := &DeleteSessionResponse{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes given bytes as DeleteSessionResponse. func (d *DeleteSessionResponse) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.Recovery: d.Recovery = i case ie.ProtocolConfigurationOptions: d.PCO = i case ie.Indication: d.IndicationFlags = i case ie.LoadControlInformation: switch i.Instance() { case 1: d.PGWNodeLoadControlInformation = i case 2: d.PGWAPNLoadControlInformation = i case 3: d.SGWNodeLoadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 1: d.PGWOverloadControlInformation = i case 2: d.SGWOverloadControlInformation = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } case ie.ExtendedProtocolConfigurationOptions: d.EPCO = i case ie.APNRateControlStatus: d.APNRateControlStatus = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (d *DeleteSessionResponse) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.Recovery; ie != nil { l += ie.MarshalLen() } if ie := d.PCO; ie != nil { l += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := d.PGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.PGWAPNLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.PGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.EPCO; ie != nil { l += ie.MarshalLen() } if ie := d.APNRateControlStatus; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DeleteSessionResponse) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (d *DeleteSessionResponse) MessageTypeName() string { return "Delete Session Response" } // TEID returns the TEID in uint32. func (d *DeleteSessionResponse) TEID() uint32 { return d.Header.teid() } ================================================ FILE: gtpv2/message/delete-session-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes DeleteSessionResponse into bytes. // // Deprecated: use DeleteSessionResponse.Marshal instead. func (d *DeleteSessionResponse) Serialize() ([]byte, error) { log.Println("DeleteSessionResponse.Serialize is deprecated. use DeleteSessionResponse.Marshal instead") return d.Marshal() } // SerializeTo serializes DeleteSessionResponse into bytes given as b. // // Deprecated: use DeleteSessionResponse.MarshalTo instead. func (d *DeleteSessionResponse) SerializeTo(b []byte) error { log.Println("DeleteSessionResponse.SerializeTo is deprecated. use DeleteSessionResponse.MarshalTo instead") return d.MarshalTo(b) } // DecodeDeleteSessionResponse decodes bytes as DeleteSessionResponse. // // Deprecated: use ParseDeleteSessionResponse instead. func DecodeDeleteSessionResponse(b []byte) (*DeleteSessionResponse, error) { log.Println("DecodeDeleteSessionResponse is deprecated. use ParseDeleteSessionResponse instead") return ParseDeleteSessionResponse(b) } // DecodeFromBytes decodes bytes as DeleteSessionResponse. // // Deprecated: use DeleteSessionResponse.UnmarshalBinary instead. func (d *DeleteSessionResponse) DecodeFromBytes(b []byte) error { log.Println("DeleteSessionResponse.DecodeFromBytes is deprecated. use DeleteSessionResponse.UnmarshalBinary instead") return d.UnmarshalBinary(b) } // Len returns the actual length of DeleteSessionResponse. // // Deprecated: use DeleteSessionResponse.MarshalLen instead. func (d *DeleteSessionResponse) Len() int { log.Println("DeleteSessionResponse.Len is deprecated. use DeleteSessionResponse.MarshalLen instead") return d.MarshalLen() } ================================================ FILE: gtpv2/message/delete-session-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDeleteSessionResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/CauseOnly", Structured: message.NewDeleteSessionResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ), Serialized: []byte{ // Header 0x48, 0x25, 0x00, 0x0e, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDeleteSessionResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/detach-acknowledge.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DetachAcknowledge is a DetachAcknowledge Header and its IEs above. type DetachAcknowledge struct { *Header Cause *ie.IE Recovery *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDetachAcknowledge creates a new DetachAcknowledge. func NewDetachAcknowledge(teid, seq uint32, ies ...*ie.IE) *DetachAcknowledge { m := &DetachAcknowledge{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDetachAcknowledge, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.Recovery: m.Recovery = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal returns the byte sequence generated from a DetachAcknowledge. func (m *DetachAcknowledge) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (m *DetachAcknowledge) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.Cause; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.Recovery; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseDetachAcknowledge decodes a given byte sequence as a DetachAcknowledge. func ParseDetachAcknowledge(b []byte) (*DetachAcknowledge, error) { m := &DetachAcknowledge{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given byte sequence as DetachAcknowledge. func (m *DetachAcknowledge) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.Recovery: m.Recovery = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (m *DetachAcknowledge) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.Cause; ie != nil { l += ie.MarshalLen() } if ie := m.Recovery; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *DetachAcknowledge) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *DetachAcknowledge) MessageTypeName() string { return "Detach Acknowledge" } // TEID returns the TEID in uint32. func (m *DetachAcknowledge) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/detach-acknowledge_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDetachAcknowledge(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDetachAcknowledge( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewRecovery(0xff), ), Serialized: []byte{ // Header 0x48, 0x96, 0x00, 0x13, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // Recovery 0x03, 0x00, 0x01, 0x00, 0xff, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDetachAcknowledge(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/detach-notification.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DetachNotification is a DetachNotification Header and its IEs above. type DetachNotification struct { *Header Cause *ie.IE DetachType *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDetachNotification creates a new DetachNotification. func NewDetachNotification(teid, seq uint32, ies ...*ie.IE) *DetachNotification { m := &DetachNotification{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDetachNotification, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.DetachType: m.DetachType = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes DetachNotification into bytes. func (m *DetachNotification) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DetachNotification into bytes. func (m *DetachNotification) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.Cause; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.DetachType; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseDetachNotification decodes given bytes as DetachNotification. func ParseDetachNotification(b []byte) (*DetachNotification, error) { m := &DetachNotification{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as DetachNotification. func (m *DetachNotification) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.DetachType: m.DetachType = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *DetachNotification) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.Cause; ie != nil { l += ie.MarshalLen() } if ie := m.DetachType; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *DetachNotification) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *DetachNotification) MessageTypeName() string { return "Detach Notification" } // TEID returns the TEID in uint32. func (m *DetachNotification) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/detach-notification_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDetachNotification(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDetachNotification( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewDetachType(gtpv2.DetachTypePS), ), Serialized: []byte{ // Header 0x48, 0x95, 0x00, 0x13, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // Detach Type 0x96, 0x00, 0x01, 0x00, 0x01, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDetachNotification(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/downlink-data-notification-ack.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DownlinkDataNotificationAcknowledge is a DownlinkDataNotificationAcknowledge Header and its IEs above. type DownlinkDataNotificationAcknowledge struct { *Header Cause *ie.IE DataNotificationDelay *ie.IE Recovery *ie.IE DLLowPriorityTrafficThrottling *ie.IE IMSI *ie.IE DLBufferingDuration *ie.IE DLBufferingSuggestedPacketCount *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDownlinkDataNotificationAcknowledge creates a new DownlinkDataNotificationAcknowledge. func NewDownlinkDataNotificationAcknowledge(teid, seq uint32, ies ...*ie.IE) *DownlinkDataNotificationAcknowledge { d := &DownlinkDataNotificationAcknowledge{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDownlinkDataNotificationAcknowledge, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.DelayValue: d.DataNotificationDelay = i case ie.Recovery: d.Recovery = i case ie.Throttling: d.DLLowPriorityTrafficThrottling = i case ie.IMSI: d.IMSI = i case ie.EPCTimer: d.DLBufferingDuration = i case ie.IntegerNumber: d.DLBufferingSuggestedPacketCount = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal serializes DownlinkDataNotificationAcknowledge into bytes. func (d *DownlinkDataNotificationAcknowledge) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DownlinkDataNotificationAcknowledge into bytes. func (d *DownlinkDataNotificationAcknowledge) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.DataNotificationDelay; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.Recovery; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.DLLowPriorityTrafficThrottling; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.IMSI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.DLBufferingDuration; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.DLBufferingSuggestedPacketCount; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDownlinkDataNotificationAcknowledge decodes given bytes as DownlinkDataNotificationAcknowledge. func ParseDownlinkDataNotificationAcknowledge(b []byte) (*DownlinkDataNotificationAcknowledge, error) { d := &DownlinkDataNotificationAcknowledge{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes given bytes as DownlinkDataNotificationAcknowledge. func (d *DownlinkDataNotificationAcknowledge) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.DelayValue: d.DataNotificationDelay = i case ie.Recovery: d.Recovery = i case ie.Throttling: d.DLLowPriorityTrafficThrottling = i case ie.IMSI: d.IMSI = i case ie.EPCTimer: d.DLBufferingDuration = i case ie.IntegerNumber: d.DLBufferingSuggestedPacketCount = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (d *DownlinkDataNotificationAcknowledge) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.DataNotificationDelay; ie != nil { l += ie.MarshalLen() } if ie := d.Recovery; ie != nil { l += ie.MarshalLen() } if ie := d.DLLowPriorityTrafficThrottling; ie != nil { l += ie.MarshalLen() } if ie := d.IMSI; ie != nil { l += ie.MarshalLen() } if ie := d.DLBufferingDuration; ie != nil { l += ie.MarshalLen() } if ie := d.DLBufferingSuggestedPacketCount; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DownlinkDataNotificationAcknowledge) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (d *DownlinkDataNotificationAcknowledge) MessageTypeName() string { return "Downlink Data Notification Acknowledge" } // TEID returns the TEID in uint32. func (d *DownlinkDataNotificationAcknowledge) TEID() uint32 { return d.Header.teid() } ================================================ FILE: gtpv2/message/downlink-data-notification-ack_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDownlinkDataNotificationAcknowledge(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDownlinkDataNotificationAcknowledge( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewDelayValue(500*time.Millisecond), ie.NewRecovery(0xff), ie.NewThrottling(20*time.Hour, 80), ie.NewIMSI("123451234567890"), ie.NewEPCTimer(20*time.Hour), ie.NewIntegerNumber(2020), ), Serialized: []byte{ // Header 0x48, 0xb1, 0x00, 0x35, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // DataNotificationDelay 0x5c, 0x00, 0x01, 0x00, 0x0a, // Recovery 0x03, 0x00, 0x01, 0x00, 0xff, // DLLowPriorityTrafficThrottling 0x9a, 0x00, 0x02, 0x00, 0x82, 0x50, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, // DLBufferingDuration 0x9c, 0x00, 0x01, 0x00, 0x82, // DLBufferingSuggestedPacketCount 0xbb, 0x00, 0x02, 0x00, 0x07, 0xe4, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDownlinkDataNotificationAcknowledge(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/downlink-data-notification-failure-indication.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DownlinkDataNotificationFailureIndication is a DownlinkDataNotificationFailureIndication Header and its IEs above. type DownlinkDataNotificationFailureIndication struct { *Header Cause *ie.IE OriginatingNode *ie.IE IMSI *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDownlinkDataNotificationFailureIndication creates a new DownlinkDataNotificationFailureIndication. func NewDownlinkDataNotificationFailureIndication(teid, seq uint32, ies ...*ie.IE) *DownlinkDataNotificationFailureIndication { d := &DownlinkDataNotificationFailureIndication{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDownlinkDataNotificationFailureIndication, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.NodeType: d.OriginatingNode = i case ie.IMSI: d.IMSI = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal serializes DownlinkDataNotificationFailureIndication into bytes. func (d *DownlinkDataNotificationFailureIndication) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DownlinkDataNotificationFailureIndication into bytes. func (d *DownlinkDataNotificationFailureIndication) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.OriginatingNode; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.IMSI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDownlinkDataNotificationFailureIndication decodes given bytes as DownlinkDataNotificationFailureIndication. func ParseDownlinkDataNotificationFailureIndication(b []byte) (*DownlinkDataNotificationFailureIndication, error) { d := &DownlinkDataNotificationFailureIndication{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes given bytes as DownlinkDataNotificationFailureIndication. func (d *DownlinkDataNotificationFailureIndication) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.NodeType: d.OriginatingNode = i case ie.IMSI: d.IMSI = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (d *DownlinkDataNotificationFailureIndication) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.OriginatingNode; ie != nil { l += ie.MarshalLen() } if ie := d.IMSI; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DownlinkDataNotificationFailureIndication) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (d *DownlinkDataNotificationFailureIndication) MessageTypeName() string { return "Downlink Data Notification Failure Indication" } // TEID returns the TEID in uint32. func (d *DownlinkDataNotificationFailureIndication) TEID() uint32 { return d.Header.teid() } ================================================ FILE: gtpv2/message/downlink-data-notification-failure-indication_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDownlinkDataNotificationFailureIndication(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDownlinkDataNotificationFailureIndication( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewNodeType(gtpv2.NodeTypeMME), ie.NewIMSI("123451234567890"), ), Serialized: []byte{ // Header 0x48, 0x46, 0x00, 0x1f, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // NodeType 0x87, 0x00, 0x01, 0x00, 0x01, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDownlinkDataNotificationFailureIndication(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/downlink-data-notification.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // DownlinkDataNotification is a DownlinkDataNotification Header and its IEs above. type DownlinkDataNotification struct { *Header Cause *ie.IE EPSBearerID *ie.IE AllocationRetensionPriority *ie.IE IMSI *ie.IE SenderFTEIDC *ie.IE IndicationFlags *ie.IE SGWNodeLoadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE PagingAndServiceInformation *ie.IE DLDataPacketsSize *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewDownlinkDataNotification creates a new DownlinkDataNotification. func NewDownlinkDataNotification(teid, seq uint32, ies ...*ie.IE) *DownlinkDataNotification { d := &DownlinkDataNotification{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeDownlinkDataNotification, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.EPSBearerID: d.EPSBearerID = i case ie.AllocationRetensionPriority: d.AllocationRetensionPriority = i case ie.IMSI: d.IMSI = i case ie.FullyQualifiedTEID: d.SenderFTEIDC = i case ie.Indication: d.IndicationFlags = i case ie.LoadControlInformation: d.SGWNodeLoadControlInformation = i case ie.OverloadControlInformation: d.SGWOverloadControlInformation = i case ie.PagingAndServiceInformation: d.PagingAndServiceInformation = i case ie.IntegerNumber: d.DLDataPacketsSize = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } d.SetLength() return d } // Marshal serializes DownlinkDataNotification into bytes. func (d *DownlinkDataNotification) Marshal() ([]byte, error) { b := make([]byte, d.MarshalLen()) if err := d.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes DownlinkDataNotification into bytes. func (d *DownlinkDataNotification) MarshalTo(b []byte) error { if d.Header.Payload != nil { d.Header.Payload = nil } d.Header.Payload = make([]byte, d.MarshalLen()-d.Header.MarshalLen()) offset := 0 if ie := d.Cause; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.EPSBearerID; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.AllocationRetensionPriority; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.IMSI; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SenderFTEIDC; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PagingAndServiceInformation; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.DLDataPacketsSize; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { if err := ie.MarshalTo(d.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(d.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } d.Header.SetLength() return d.Header.MarshalTo(b) } // ParseDownlinkDataNotification decodes given bytes as DownlinkDataNotification. func ParseDownlinkDataNotification(b []byte) (*DownlinkDataNotification, error) { d := &DownlinkDataNotification{} if err := d.UnmarshalBinary(b); err != nil { return nil, err } return d, nil } // UnmarshalBinary decodes given bytes as DownlinkDataNotification. func (d *DownlinkDataNotification) UnmarshalBinary(b []byte) error { var err error d.Header, err = ParseHeader(b) if err != nil { return err } if len(d.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(d.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: d.Cause = i case ie.EPSBearerID: d.EPSBearerID = i case ie.AllocationRetensionPriority: d.AllocationRetensionPriority = i case ie.IMSI: d.IMSI = i case ie.FullyQualifiedTEID: d.SenderFTEIDC = i case ie.Indication: d.IndicationFlags = i case ie.LoadControlInformation: d.SGWNodeLoadControlInformation = i case ie.OverloadControlInformation: d.SGWOverloadControlInformation = i case ie.PagingAndServiceInformation: d.PagingAndServiceInformation = i case ie.IntegerNumber: d.DLDataPacketsSize = i case ie.PrivateExtension: d.PrivateExtension = i default: d.AdditionalIEs = append(d.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (d *DownlinkDataNotification) MarshalLen() int { l := d.Header.MarshalLen() - len(d.Header.Payload) if ie := d.Cause; ie != nil { l += ie.MarshalLen() } if ie := d.EPSBearerID; ie != nil { l += ie.MarshalLen() } if ie := d.AllocationRetensionPriority; ie != nil { l += ie.MarshalLen() } if ie := d.IMSI; ie != nil { l += ie.MarshalLen() } if ie := d.SenderFTEIDC; ie != nil { l += ie.MarshalLen() } if ie := d.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := d.SGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := d.PagingAndServiceInformation; ie != nil { l += ie.MarshalLen() } if ie := d.DLDataPacketsSize; ie != nil { l += ie.MarshalLen() } if ie := d.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range d.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (d *DownlinkDataNotification) SetLength() { d.Header.Length = uint16(d.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (d *DownlinkDataNotification) MessageTypeName() string { return "Downlink Data Notification" } // TEID returns the TEID in uint32. func (d *DownlinkDataNotification) TEID() uint32 { return d.Header.teid() } ================================================ FILE: gtpv2/message/downlink-data-notification_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestDownlinkDataNotification(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewDownlinkDataNotification( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(0x05), ie.NewAllocationRetensionPriority(1, 2, 1), ie.NewIMSI("123451234567890"), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS4SGSNGTPU, 0xffffffff, "1.1.1.1", ""), ie.NewIndicationFromOctets(0xa1, 0x08), ie.NewPagingAndServiceInformation(5, 0x01, 0xff), ie.NewIntegerNumber(2020), ), Serialized: []byte{ // Header 0x48, 0xb0, 0x00, 0x44, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // ARP 0x9b, 0x00, 0x01, 0x00, 0x49, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, // F-TEID 0x57, 0x00, 0x09, 0x00, 0x8f, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, // Indication 0x4d, 0x00, 0x02, 0x00, 0xa1, 0x08, // PagingAndServiceInformation 0xba, 0x00, 0x03, 0x00, 0x05, 0x01, 0x7f, // DLDataPacketSize 0xbb, 0x00, 0x02, 0x00, 0x07, 0xe4, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseDownlinkDataNotification(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/echo-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // EchoRequest is a EchoRequest Header and its IEs above. type EchoRequest struct { *Header Recovery *ie.IE SendingNodeFeatures *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewEchoRequest creates a new EchoRequest. func NewEchoRequest(seq uint32, ies ...*ie.IE) *EchoRequest { e := &EchoRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 0), MsgTypeEchoRequest, 0, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Recovery: e.Recovery = i case ie.NodeFeatures: e.SendingNodeFeatures = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } e.SetLength() return e } // Marshal returns the byte sequence generated from a EchoRequest. func (e *EchoRequest) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *EchoRequest) MarshalTo(b []byte) error { if e.Header.Payload != nil { e.Header.Payload = nil } e.Header.Payload = make([]byte, e.MarshalLen()-e.Header.MarshalLen()) offset := 0 if ie := e.Recovery; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := e.SendingNodeFeatures; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } e.Header.SetLength() return e.Header.MarshalTo(b) } // ParseEchoRequest decodes a given byte sequence as a EchoRequest. func ParseEchoRequest(b []byte) (*EchoRequest, error) { e := &EchoRequest{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary decodes a given byte sequence as a EchoRequest. func (e *EchoRequest) UnmarshalBinary(b []byte) error { var err error e.Header, err = ParseHeader(b) if err != nil { return err } if len(e.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(e.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Recovery: e.Recovery = i case ie.NodeFeatures: e.SendingNodeFeatures = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (e *EchoRequest) MarshalLen() int { l := e.Header.MarshalLen() - len(e.Header.Payload) if ie := e.Recovery; ie != nil { l += ie.MarshalLen() } if ie := e.SendingNodeFeatures; ie != nil { l += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (e *EchoRequest) SetLength() { e.Header.Length = uint16(e.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (e *EchoRequest) MessageTypeName() string { return "Echo Request" } // TEID returns the TEID in uint32. func (e *EchoRequest) TEID() uint32 { return e.Header.teid() } ================================================ FILE: gtpv2/message/echo-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes EchoRequest into bytes. // // Deprecated: use EchoRequest.Marshal instead. func (e *EchoRequest) Serialize() ([]byte, error) { log.Println("EchoRequest.Serialize is deprecated. use EchoRequest.Marshal instead") return e.Marshal() } // SerializeTo serializes EchoRequest into bytes given as b. // // Deprecated: use EchoRequest.MarshalTo instead. func (e *EchoRequest) SerializeTo(b []byte) error { log.Println("EchoRequest.SerializeTo is deprecated. use EchoRequest.MarshalTo instead") return e.MarshalTo(b) } // DecodeEchoRequest decodes bytes as EchoRequest. // // Deprecated: use ParseEchoRequest instead. func DecodeEchoRequest(b []byte) (*EchoRequest, error) { log.Println("DecodeEchoRequest is deprecated. use ParseEchoRequest instead") return ParseEchoRequest(b) } // DecodeFromBytes decodes bytes as EchoRequest. // // Deprecated: use EchoRequest.UnmarshalBinary instead. func (e *EchoRequest) DecodeFromBytes(b []byte) error { log.Println("EchoRequest.DecodeFromBytes is deprecated. use EchoRequest.UnmarshalBinary instead") return e.UnmarshalBinary(b) } // Len returns the actual length of EchoRequest. // // Deprecated: use EchoRequest.MarshalLen instead. func (e *EchoRequest) Len() int { log.Println("EchoRequest.Len is deprecated. use EchoRequest.MarshalLen instead") return e.MarshalLen() } ================================================ FILE: gtpv2/message/echo-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestEchoRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewEchoRequest( 0, ie.NewRecovery(0x80), ie.NewNodeFeatures(0x01), ), Serialized: []byte{ 0x40, 0x01, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, // Recovery 0x03, 0x00, 0x01, 0x00, 0x80, // Node Features 0x98, 0x00, 0x01, 0x00, 0x01, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseEchoRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/echo-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // EchoResponse is a EchoResponse Header and its IEs above. type EchoResponse struct { *Header Recovery *ie.IE SendingNodeFeatures *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewEchoResponse creates a new EchoResponse. func NewEchoResponse(seq uint32, ies ...*ie.IE) *EchoResponse { e := &EchoResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 0), MsgTypeEchoResponse, 0, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Recovery: e.Recovery = i case ie.NodeFeatures: e.SendingNodeFeatures = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } e.SetLength() return e } // Marshal returns the byte sequence generated from a EchoResponse. func (e *EchoResponse) Marshal() ([]byte, error) { b := make([]byte, e.MarshalLen()) if err := e.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (e *EchoResponse) MarshalTo(b []byte) error { if e.Header.Payload != nil { e.Header.Payload = nil } e.Header.Payload = make([]byte, e.MarshalLen()-e.Header.MarshalLen()) offset := 0 if ie := e.Recovery; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := e.SendingNodeFeatures; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(e.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } e.Header.SetLength() return e.Header.MarshalTo(b) } // ParseEchoResponse decodes a given byte sequence as a EchoResponse. func ParseEchoResponse(b []byte) (*EchoResponse, error) { e := &EchoResponse{} if err := e.UnmarshalBinary(b); err != nil { return nil, err } return e, nil } // UnmarshalBinary decodes a given byte sequence as a EchoResponse. func (e *EchoResponse) UnmarshalBinary(b []byte) error { var err error e.Header, err = ParseHeader(b) if err != nil { return err } if len(e.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(e.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Recovery: e.Recovery = i case ie.NodeFeatures: e.SendingNodeFeatures = i case ie.PrivateExtension: e.PrivateExtension = i default: e.AdditionalIEs = append(e.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (e *EchoResponse) MarshalLen() int { l := e.Header.MarshalLen() - len(e.Header.Payload) if ie := e.Recovery; ie != nil { l += ie.MarshalLen() } if ie := e.SendingNodeFeatures; ie != nil { l += ie.MarshalLen() } if ie := e.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range e.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (e *EchoResponse) SetLength() { e.Header.Length = uint16(e.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (e *EchoResponse) MessageTypeName() string { return "Echo Response" } // TEID returns the TEID in uint32. func (e *EchoResponse) TEID() uint32 { return e.Header.teid() } ================================================ FILE: gtpv2/message/echo-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes EchoResponse into bytes. // // Deprecated: use EchoResponse.Marshal instead. func (e *EchoResponse) Serialize() ([]byte, error) { log.Println("EchoResponse.Serialize is deprecated. use EchoResponse.Marshal instead") return e.Marshal() } // SerializeTo serializes EchoResponse into bytes given as b. // // Deprecated: use EchoResponse.MarshalTo instead. func (e *EchoResponse) SerializeTo(b []byte) error { log.Println("EchoResponse.SerializeTo is deprecated. use EchoResponse.MarshalTo instead") return e.MarshalTo(b) } // DecodeEchoResponse decodes bytes as EchoResponse. // // Deprecated: use ParseEchoResponse instead. func DecodeEchoResponse(b []byte) (*EchoResponse, error) { log.Println("DecodeEchoResponse is deprecated. use ParseEchoResponse instead") return ParseEchoResponse(b) } // DecodeFromBytes decodes bytes as EchoResponse. // // Deprecated: use EchoResponse.UnmarshalBinary instead. func (e *EchoResponse) DecodeFromBytes(b []byte) error { log.Println("EchoResponse.DecodeFromBytes is deprecated. use EchoResponse.UnmarshalBinary instead") return e.UnmarshalBinary(b) } // Len returns the actual length of EchoResponse. // // Deprecated: use EchoResponse.MarshalLen instead. func (e *EchoResponse) Len() int { log.Println("EchoResponse.Len is deprecated. use EchoResponse.MarshalLen instead") return e.MarshalLen() } ================================================ FILE: gtpv2/message/echo-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestEchoResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewEchoResponse( 0, ie.NewRecovery(0x80), ie.NewNodeFeatures(0x01), ), Serialized: []byte{ 0x40, 0x02, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, // Recovery 0x03, 0x00, 0x01, 0x00, 0x80, // Node Features 0x98, 0x00, 0x01, 0x00, 0x01, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseEchoResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/errors.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "errors" // Error definitions. var ( ErrInvalidLength = errors.New("length value is invalid") ErrTooShortToParse = errors.New("too short to decode as GTP") ) ================================================ FILE: gtpv2/message/generic.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "fmt" "github.com/wmnsk/go-gtp/gtpv2/ie" ) // Generic is a Generic formed GTP Header and its IEs above. type Generic struct { *Header IEs []*ie.IE } // NewGeneric creates a new Generic. func NewGeneric(msgType uint8, teid, seq uint32, ie ...*ie.IE) *Generic { g := &Generic{ Header: NewHeader( NewHeaderFlags(2, 0x00, 1), msgType, teid, seq, nil, ), IEs: ie, } g.SetLength() return g } // NewGenericWithoutTEID creates a new Generic. func NewGenericWithoutTEID(msgType uint8, teid, seq uint32, ie ...*ie.IE) *Generic { g := &Generic{ Header: NewHeader( NewHeaderFlags(2, 0x00, 0), msgType, teid, seq, nil, ), IEs: ie, } return g } // Marshal returns the byte sequence generated from a Generic. func (g *Generic) Marshal() ([]byte, error) { b := make([]byte, g.MarshalLen()) if err := g.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (g *Generic) MarshalTo(b []byte) error { if g.Header.Payload != nil { g.Header.Payload = nil } g.Header.Payload = make([]byte, g.MarshalLen()-g.Header.MarshalLen()) offset := 0 for _, ie := range g.IEs { if ie == nil { continue } if err := ie.MarshalTo(g.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } g.Header.SetLength() return g.Header.MarshalTo(b) } // ParseGeneric decodes a given byte sequence as a Generic. func ParseGeneric(b []byte) (*Generic, error) { g := &Generic{} if err := g.UnmarshalBinary(b); err != nil { return nil, err } return g, nil } // UnmarshalBinary decodes a given byte sequence as a Generic. func (g *Generic) UnmarshalBinary(b []byte) error { var err error g.Header, err = ParseHeader(b) if err != nil { return err } if len(g.Header.Payload) < 2 { return nil } g.IEs, err = ie.ParseMultiIEs(g.Header.Payload) if err != nil { return err } return nil } // MarshalLen returns the serial length of Data. func (g *Generic) MarshalLen() int { l := g.Header.MarshalLen() - len(g.Header.Payload) for _, ie := range g.IEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (g *Generic) SetLength() { l := g.Header.MarshalLen() - len(g.Header.Payload) - 4 for _, ie := range g.IEs { l += ie.MarshalLen() } g.Header.Length = uint16(l) } // MessageTypeName returns the name of protocol. func (g *Generic) MessageTypeName() string { return fmt.Sprintf("Unknown (%d)", g.Header.Type) } // TEID returns the TEID in uint32. func (g *Generic) TEID() uint32 { return g.Header.teid() } // AddIE add IEs to Generic type of GTPv2 message and update Length field. func (g *Generic) AddIE(ie ...*ie.IE) { g.IEs = append(g.IEs, ie...) g.SetLength() } ================================================ FILE: gtpv2/message/generic_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes Generic into bytes. // // Deprecated: use Generic.Marshal instead. func (g *Generic) Serialize() ([]byte, error) { log.Println("Generic.Serialize is deprecated. use Generic.Marshal instead") return g.Marshal() } // SerializeTo serializes Generic into bytes given as b. // // Deprecated: use Generic.MarshalTo instead. func (g *Generic) SerializeTo(b []byte) error { log.Println("Generic.SerializeTo is deprecated. use Generic.MarshalTo instead") return g.MarshalTo(b) } // DecodeGeneric decodes bytes as Generic. // // Deprecated: use ParseGeneric instead. func DecodeGeneric(b []byte) (*Generic, error) { log.Println("DecodeGeneric is deprecated. use ParseGeneric instead") return ParseGeneric(b) } // DecodeFromBytes decodes bytes as Generic. // // Deprecated: use Generic.UnmarshalBinary instead. func (g *Generic) DecodeFromBytes(b []byte) error { log.Println("Generic.DecodeFromBytes is deprecated. use Generic.UnmarshalBinary instead") return g.UnmarshalBinary(b) } // Len returns the actual length of Generic. // // Deprecated: use Generic.MarshalLen instead. func (g *Generic) Len() int { log.Println("Generic.Len is deprecated. use Generic.MarshalLen instead") return g.MarshalLen() } ================================================ FILE: gtpv2/message/generic_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestGeneric(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewGeneric( message.MsgTypeEchoRequest, testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewRecovery(0x80), ), Serialized: []byte{ 0x48, 0x01, 0x00, 0x0d, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x80, }, }, { Description: "No-TEID", Structured: message.NewGeneric( message.MsgTypeEchoRequest, testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewRecovery(0x80), ), Serialized: []byte{ 0x48, 0x01, 0x00, 0x0d, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x80, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseGeneric(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/header.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "encoding/binary" "fmt" "github.com/wmnsk/go-gtp/utils" ) const ( fixedHeaderSize = 4 seqSpareSize = 4 teidSize = 4 noTEIDHeaderSize = fixedHeaderSize + seqSpareSize teidHeaderSize = noTEIDHeaderSize + teidSize ) // Header is a GTPv2 common header type Header struct { Flags uint8 Type uint8 Length uint16 TEID uint32 SequenceNumber uint32 Spare uint8 Payload []byte } // NewHeader creates a new Header func NewHeader(flags, mtype uint8, teid, seqnum uint32, data []byte) *Header { h := &Header{ Flags: flags, Type: mtype, TEID: teid, SequenceNumber: seqnum, Spare: 0, Payload: data, } h.SetLength() return h } // NewHeaderFlags returns a Header Flag built by its components given as arguments. func NewHeaderFlags(v, p, t int) uint8 { return uint8( ((v & 0x7) << 5) | ((p & 0x1) << 4) | ((t & 0x1) << 3), ) } // Marshal returns the byte sequence generated from a Header instance. func (h *Header) Marshal() ([]byte, error) { b := make([]byte, h.MarshalLen()) if err := h.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (h *Header) MarshalTo(b []byte) error { b[0] = h.Flags b[1] = h.Type binary.BigEndian.PutUint16(b[2:4], h.Length) offset := 4 if h.HasTEID() { binary.BigEndian.PutUint32(b[offset:offset+4], h.TEID) offset += 4 } copy(b[offset:offset+3], utils.Uint32To24(h.SequenceNumber)) b[offset+3] = h.Spare copy(b[offset+4:h.MarshalLen()], h.Payload) return nil } // ParseHeader decodes given byte sequence as a GTPv2 header. func ParseHeader(b []byte) (*Header, error) { h := &Header{} if err := h.UnmarshalBinary(b); err != nil { return nil, err } return h, nil } // UnmarshalBinary sets the values retrieved from byte sequence in GTPv2 header. func (h *Header) UnmarshalBinary(b []byte) error { l := len(b) if l < 12 { return ErrTooShortToParse } h.Flags = b[0] h.Type = b[1] h.Length = binary.BigEndian.Uint16(b[2:4]) if h.Length < seqSpareSize { return ErrTooShortToParse } if h.HasTEID() { if h.Length < seqSpareSize+teidSize { return ErrTooShortToParse } h.TEID = binary.BigEndian.Uint32(b[4:8]) h.SequenceNumber = utils.Uint24To32(b[8:11]) h.Spare = b[11] if int(h.Length)+fixedHeaderSize > l { h.Payload = b[teidHeaderSize:] return nil } if fixedHeaderSize+h.Length >= teidHeaderSize { h.Payload = b[teidHeaderSize : fixedHeaderSize+h.Length] } else { return ErrInvalidLength } return nil } h.SequenceNumber = utils.Uint24To32(b[4:7]) h.Spare = b[7] if int(h.Length)+fixedHeaderSize > l { h.Payload = b[noTEIDHeaderSize:] return nil } if fixedHeaderSize+h.Length >= noTEIDHeaderSize { h.Payload = b[noTEIDHeaderSize : fixedHeaderSize+h.Length] } else { return ErrInvalidLength } return nil } // MarshalLen returns field length in integer. func (h *Header) MarshalLen() int { l := 8 + len(h.Payload) if h.HasTEID() { l += 4 } return l } // SetLength sets the length in Length field. func (h *Header) SetLength() { h.Length = uint16(4 + len(h.Payload)) if h.HasTEID() { h.Length += 4 } } // String returns the GTPv2 header values in human readable format. func (h *Header) String() string { return fmt.Sprintf("{Flags: %#x, Type: %d, Length: %d, TEID: %#x, SequenceNumber: %#x, Spare: %d, Payload: %#v}", h.Flags, h.Type, h.Length, h.TEID, h.SequenceNumber, h.Spare, h.Payload, ) } // IsPiggybacking reports whether the message has the trailing(piggybacked) message. func (h *Header) IsPiggybacking() bool { return (int(h.Flags)>>4)&0x01 == 1 } // SetPiggybacking sets the Piggybacking flag. // // The given value should only be 0 or 1. Otherwise it may cause the unexpected result. func (h *Header) SetPiggybacking(val uint8) { h.Flags = (h.Flags & 0xef) | (val & 0x01 << 4) } // HasTEID determines whether a GTPv2 has TEID inside by checking the flag. func (h *Header) HasTEID() bool { return (int(h.Flags)>>3)&0x01 == 1 } func (h *Header) teid() uint32 { if !h.HasTEID() { return 0 } return h.TEID } // SetTEID sets the TEIDFlag to 1 and puts the TEID given into TEID field. func (h *Header) SetTEID(teid uint32) { h.Flags |= (1 << 3) h.TEID = teid } // Sequence returns SequenceNumber in uint32. func (h *Header) Sequence() uint32 { return h.SequenceNumber } // SetSequenceNumber sets the SequenceNumber in Header. func (h *Header) SetSequenceNumber(seq uint32) { h.SequenceNumber = seq } // HasMessagePriority reports whether the message has MessagePriority field func (h *Header) HasMessagePriority() bool { return (int(h.Flags)>>2)&0x01 == 1 } // SetMessagePriority sets the MessagePriorityFlag to 1 and puts the MessagePriority // given into MessagePriority field. func (h *Header) SetMessagePriority(mp uint8) { h.Flags |= (1 << 2) h.Spare = (mp & 0xf0) } // MessagePriority returns the value of MessagePriority. // // Note that this returns the value set in the field even if the MessagePriorityFlag // is not set to 1. func (h *Header) MessagePriority() uint8 { return h.Spare & 0xf0 } // Version returns the GTP version. func (h *Header) Version() int { return 2 } // MessageType returns the type of messagg. func (h *Header) MessageType() uint8 { return h.Type } ================================================ FILE: gtpv2/message/header_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes Header into bytes. // // Deprecated: use Header.Marshal instead. func (h *Header) Serialize() ([]byte, error) { log.Println("Header.Serialize is deprecated. use Header.Marshal instead") return h.Marshal() } // SerializeTo serializes Header into bytes given as b. // // Deprecated: use Header.MarshalTo instead. func (h *Header) SerializeTo(b []byte) error { log.Println("Header.SerializeTo is deprecated. use Header.MarshalTo instead") return h.MarshalTo(b) } // DecodeHeader decodes bytes as Header. // // Deprecated: use ParseHeader instead. func DecodeHeader(b []byte) (*Header, error) { log.Println("DecodeHeader is deprecated. use ParseHeader instead") return ParseHeader(b) } // DecodeFromBytes decodes bytes as Header. // // Deprecated: use Header.UnmarshalBinary instead. func (h *Header) DecodeFromBytes(b []byte) error { log.Println("Header.DecodeFromBytes is deprecated. use Header.UnmarshalBinary instead") return h.UnmarshalBinary(b) } // Len returns the actual length of Header. // // Deprecated: use Header.MarshalLen instead. func (h *Header) Len() int { log.Println("Header.Len is deprecated. use Header.MarshalLen instead") return h.MarshalLen() } ================================================ FILE: gtpv2/message/header_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestHeader(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewHeader( message.NewHeaderFlags(2, 0, 1), 32, // Message type 0xffffffff, // TEID 0xdadada, // Sequence Number []byte{ // Payload: IMSI IE 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, }, ), Serialized: []byte{ 0x48, 0x20, 0x00, 0x14, 0xff, 0xff, 0xff, 0xff, 0xda, 0xda, 0xda, 0x00, 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseHeader(b) if err != nil { return nil, err } return v, nil }) } ================================================ FILE: gtpv2/message/message.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. /* Package message provides encoding/decoding feature of GTPv2 protocol. */ package message import ( "fmt" "io" "reflect" "strings" "github.com/wmnsk/go-gtp/gtpv2/ie" ) // Message Type definitions. const ( _ uint8 = iota MsgTypeEchoRequest MsgTypeEchoResponse MsgTypeVersionNotSupportedIndication MsgTypeDirectTransferRequest MsgTypeDirectTransferResponse MsgTypeNotificationRequest MsgTypeNotificationResponse _ _ _ _ _ _ _ _ _ // 8-16: Reserved for S101 interface MsgTypeRIMInformationTransfer _ _ _ _ _ _ _ // 18-24: Reserved for S121 interface MsgTypeSRVCCPsToCsRequest MsgTypeSRVCCPsToCsResponse MsgTypeSRVCCPsToCsCompleteNotification MsgTypeSRVCCPsToCsCompleteAcknowledge MsgTypeSRVCCPsToCsCancelNotification MsgTypeSRVCCPsToCsCancelAcknowledge MsgTypeSRVCCCsToPsRequest MsgTypeCreateSessionRequest MsgTypeCreateSessionResponse MsgTypeModifyBearerRequest MsgTypeModifyBearerResponse MsgTypeDeleteSessionRequest MsgTypeDeleteSessionResponse MsgTypeChangeNotificationRequest MsgTypeChangeNotificationResponse MsgTypeRemoteUEReportNotification MsgTypeRemoteUEReportAcknowledge _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ // 42-63: Reserved for S4/S11, S5/S8 interfaces MsgTypeModifyBearerCommand MsgTypeModifyBearerFailureIndication MsgTypeDeleteBearerCommand MsgTypeDeleteBearerFailureIndication MsgTypeBearerResourceCommand MsgTypeBearerResourceFailureIndication MsgTypeDownlinkDataNotificationFailureIndication MsgTypeTraceSessionActivation MsgTypeTraceSessionDeactivation MsgTypeStopPagingIndication _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ // 74-94: Reserved for GTPv2 non-specific interfaces MsgTypeCreateBearerRequest MsgTypeCreateBearerResponse MsgTypeUpdateBearerRequest MsgTypeUpdateBearerResponse MsgTypeDeleteBearerRequest MsgTypeDeleteBearerResponse MsgTypeDeletePDNConnectionSetRequest MsgTypeDeletePDNConnectionSetResponse MsgTypePGWDownlinkTriggeringNotification MsgTypePGWDownlinkTriggeringAcknowledge _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ // 105-127: Reserved for S5, S4/S11 interfaces MsgTypeIdentificationRequest MsgTypeIdentificationResponse MsgTypeContextRequest MsgTypeContextResponse MsgTypeContextAcknowledge MsgTypeForwardRelocationRequest MsgTypeForwardRelocationResponse MsgTypeForwardRelocationCompleteNotification MsgTypeForwardRelocationCompleteAcknowledge MsgTypeForwardAccessContextNotification MsgTypeForwardAccessContextAcknowledge MsgTypeRelocationCancelRequest MsgTypeRelocationCancelResponse MsgTypeConfigurationTransferTunnel _ _ _ _ _ _ _ // 142-148: Reserved for S3/S10/S16 interfaces MsgTypeDetachNotification MsgTypeDetachAcknowledge MsgTypeCSPagingIndication MsgTypeRANInformationRelay MsgTypeAlertMMENotification MsgTypeAlertMMEAcknowledge MsgTypeUEActivityNotification MsgTypeUEActivityAcknowledge MsgTypeISRStatusIndication MsgTypeUERegistrationQueryRequest MsgTypeUERegistrationQueryResponse MsgTypeCreateForwardingTunnelRequest MsgTypeCreateForwardingTunnelResponse MsgTypeSuspendNotification MsgTypeSuspendAcknowledge MsgTypeResumeNotification MsgTypeResumeAcknowledge MsgTypeCreateIndirectDataForwardingTunnelRequest MsgTypeCreateIndirectDataForwardingTunnelResponse MsgTypeDeleteIndirectDataForwardingTunnelRequest MsgTypeDeleteIndirectDataForwardingTunnelResponse MsgTypeReleaseAccessBearersRequest MsgTypeReleaseAccessBearersResponse _ _ _ _ // 172-175: Reserved for S4/S11 interfaces MsgTypeDownlinkDataNotification MsgTypeDownlinkDataNotificationAcknowledge _ MsgTypePGWRestartNotification MsgTypePGWRestartNotificationAcknowledge _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ // 181-199: Reserved for S4 interface MsgTypeUpdatePDNConnectionSetRequest MsgTypeUpdatePDNConnectionSetResponse _ _ _ _ _ _ _ _ _ // 202-210: Reserved for S5/S8 interfaces MsgTypeModifyAccessBearersRequest MsgTypeModifyAccessBearersResponse _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ // 213-230: Reserved for S11 interface MsgTypeMBMSSessionStartRequest MsgTypeMBMSSessionStartResponse MsgTypeMBMSSessionUpdateRequest MsgTypeMBMSSessionUpdateResponse MsgTypeMBMSSessionStopRequest MsgTypeMBMSSessionStopResponse _ _ _ // 237-239: Reserved for Sm/Sn interface MsgTypeSRVCCCsToPsResponse MsgTypeSRVCCCsToPsCompleteNotification MsgTypeSRVCCCsToPsCompleteAcknowledge MsgTypeSRVCCCsToPsCancelNotification MsgTypeSRVCCCsToPsCancelAcknowledge _ _ _ // 245-247: Reserved for Sv interface _ _ _ _ _ _ _ _ // 248-255: Reserved for others ) // Message is an interface that defines GTPv2 message. type Message interface { MarshalTo([]byte) error UnmarshalBinary(b []byte) error MarshalLen() int Version() int MessageType() uint8 MessageTypeName() string TEID() uint32 SetTEID(uint32) Sequence() uint32 SetSequenceNumber(uint32) // deprecated SerializeTo([]byte) error DecodeFromBytes(b []byte) error } // Marshal returns the byte sequence generated from a Message instance. // Better to use MarshalXxx instead if you know the name of message to be serialized. func Marshal(m Message) ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // Parse decodes the given bytes as Message. func Parse(b []byte) (Message, error) { var m Message if len(b) < 2 { return nil, io.ErrUnexpectedEOF } switch b[1] { case MsgTypeEchoRequest: m = &EchoRequest{} case MsgTypeEchoResponse: m = &EchoResponse{} case MsgTypeVersionNotSupportedIndication: m = &VersionNotSupportedIndication{} case MsgTypeCreateSessionRequest: m = &CreateSessionRequest{} case MsgTypeCreateSessionResponse: m = &CreateSessionResponse{} case MsgTypeDeleteSessionRequest: m = &DeleteSessionRequest{} case MsgTypeDeleteSessionResponse: m = &DeleteSessionResponse{} case MsgTypeModifyBearerCommand: m = &ModifyBearerCommand{} case MsgTypeModifyBearerFailureIndication: m = &ModifyBearerFailureIndication{} case MsgTypeDeleteBearerCommand: m = &DeleteBearerCommand{} case MsgTypeDeleteBearerFailureIndication: m = &DeleteBearerFailureIndication{} case MsgTypeDeleteBearerRequest: m = &DeleteBearerRequest{} case MsgTypeCreateBearerRequest: m = &CreateBearerRequest{} case MsgTypeCreateBearerResponse: m = &CreateBearerResponse{} case MsgTypeDeleteBearerResponse: m = &DeleteBearerResponse{} case MsgTypeModifyBearerRequest: m = &ModifyBearerRequest{} case MsgTypeModifyBearerResponse: m = &ModifyBearerResponse{} case MsgTypeUpdateBearerRequest: m = &UpdateBearerRequest{} case MsgTypeUpdateBearerResponse: m = &UpdateBearerResponse{} case MsgTypeContextRequest: m = &ContextRequest{} case MsgTypeContextResponse: m = &ContextResponse{} case MsgTypeContextAcknowledge: m = &ContextAcknowledge{} case MsgTypeReleaseAccessBearersRequest: m = &ReleaseAccessBearersRequest{} case MsgTypeReleaseAccessBearersResponse: m = &ReleaseAccessBearersResponse{} case MsgTypeStopPagingIndication: m = &StopPagingIndication{} case MsgTypeModifyAccessBearersRequest: m = &ModifyAccessBearersRequest{} case MsgTypeModifyAccessBearersResponse: m = &ModifyAccessBearersResponse{} case MsgTypeDeletePDNConnectionSetRequest: m = &DeletePDNConnectionSetRequest{} case MsgTypeDeletePDNConnectionSetResponse: m = &DeletePDNConnectionSetResponse{} case MsgTypeUpdatePDNConnectionSetRequest: m = &UpdatePDNConnectionSetRequest{} case MsgTypeUpdatePDNConnectionSetResponse: m = &UpdatePDNConnectionSetResponse{} case MsgTypePGWRestartNotification: m = &PGWRestartNotification{} case MsgTypePGWRestartNotificationAcknowledge: m = &PGWRestartNotificationAcknowledge{} case MsgTypeDetachNotification: m = &DetachNotification{} case MsgTypeDetachAcknowledge: m = &DetachAcknowledge{} case MsgTypeResumeAcknowledge: m = &ResumeAcknowledge{} case MsgTypeResumeNotification: m = &ResumeNotification{} case MsgTypeSuspendAcknowledge: m = &SuspendAcknowledge{} case MsgTypeSuspendNotification: m = &SuspendNotification{} case MsgTypeChangeNotificationRequest: m = &ChangeNotificationRequest{} case MsgTypeChangeNotificationResponse: m = &ChangeNotificationResponse{} case MsgTypeDownlinkDataNotification: m = &DownlinkDataNotification{} case MsgTypeDownlinkDataNotificationAcknowledge: m = &DownlinkDataNotificationAcknowledge{} case MsgTypeDownlinkDataNotificationFailureIndication: m = &DownlinkDataNotificationFailureIndication{} default: m = &Generic{} } if err := m.UnmarshalBinary(b); err != nil { return nil, fmt.Errorf("failed to decode GTPv2 Message: %w", err) } return m, nil } // Prettify returns a Message in prettified representation in string. // // Note that this relies much on reflect package, and thus the frequent use of // this function may have a serious impact on the performance of your software. func Prettify(m Message) string { name := m.MessageTypeName() header := strings.TrimSuffix(fmt.Sprint(m), "}") v := reflect.Indirect(reflect.ValueOf(m)) n := v.NumField() - 1 fields := make([]*field, n) for i := 1; i < n+1; i++ { // Skip *Header fields[i-1] = &field{name: v.Type().Field(i).Name, maybeIE: v.Field(i).Interface()} } return fmt.Sprintf("{%s: %s, IEs: [%v]}", name, header, strings.Join(prettifyFields(fields), ", ")) } type field struct { name string maybeIE interface{} } func prettifyFields(fields []*field) []string { ret := []string{} for _, field := range fields { if field.maybeIE == nil { ret = append(ret, prettifyIE(field.name, nil)) continue } // TODO: do this recursively? v, ok := field.maybeIE.(*ie.IE) if !ok { // only for AdditionalIEs field if ies, ok := field.maybeIE.([]*ie.IE); ok { vals := make([]string, len(ies)) for i, val := range ies { vals[i] = fmt.Sprint(val) } ret = append(ret, fmt.Sprintf("{%s: [%v]}", field.name, strings.Join(vals, ", "))) } continue } ret = append(ret, prettifyIE(field.name, v)) } return ret } func prettifyIE(name string, i *ie.IE) string { if i == nil { return fmt.Sprintf("{%s: %v}", name, i) } if i.IsGrouped() { vals := make([]string, len(i.ChildIEs)) for i, val := range i.ChildIEs { vals[i] = fmt.Sprint(val) } return fmt.Sprintf("{%s: [%v]}", name, strings.Join(vals, ", ")) } return fmt.Sprintf("{%s: %v}", name, i) } ================================================ FILE: gtpv2/message/message_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes Message into bytes. // // Deprecated: use Marshal instead. func Serialize(m Message) ([]byte, error) { log.Println("Serialize is deprecated. use Marshal instead") return Marshal(m) } // Decode decodes bytes as Message. // // Deprecated: use Parse instead. func Decode(b []byte) (Message, error) { log.Println("Decode is deprecated. use Parse instead") return Parse(b) } ================================================ FILE: gtpv2/message/message_fuzz_test.go ================================================ //go:build go1.18 package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/message" ) func FuzzParse(f *testing.F) { testcases := [][]byte{ {0x10, 0x20, 0x30}, {0x48, 0x20, 0x00, 0x14, 0xff, 0xff, 0xff, 0xff, 0xda, 0xda, 0xda}, } for _, tc := range testcases { f.Add(tc) } f.Fuzz(func(t *testing.T, data []byte) { if v, err := message.Parse(data); err == nil && v == nil { t.Errorf("nil without error") } }) } func FuzzHeaderParse(f *testing.F) { f.Fuzz(func(t *testing.T, b []byte) { if _, err := message.ParseHeader(b); err != nil { t.Skip() } }) } ================================================ FILE: gtpv2/message/message_test.go ================================================ package message_test import ( "net" ) var ( mac1, _ = net.ParseMAC("12:34:56:78:90:01") mac2, _ = net.ParseMAC("12:34:56:78:90:02") ) ================================================ FILE: gtpv2/message/modify-access-bearers-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // ModifyAccessBearersRequest is a ModifyAccessBearersRequest Header and its IEs above. type ModifyAccessBearersRequest struct { *Header IndicationFlags *ie.IE SenderFTEIDC *ie.IE DelayDownlinkPacketNotificationRequest *ie.IE BearerContextsToBeModified []*ie.IE BearerContextsToBeRemoved []*ie.IE Recovery *ie.IE SecondaryRATUsageDataReport []*ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewModifyAccessBearersRequest creates a new ModifyAccessBearersRequest. func NewModifyAccessBearersRequest(teid, seq uint32, ies ...*ie.IE) *ModifyAccessBearersRequest { m := &ModifyAccessBearersRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeModifyAccessBearersRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Indication: m.IndicationFlags = i case ie.NodeType: m.SenderFTEIDC = i case ie.DelayValue: m.DelayDownlinkPacketNotificationRequest = i case ie.BearerContext: switch i.Instance() { case 0: m.BearerContextsToBeModified = append(m.BearerContextsToBeModified, i) case 1: m.BearerContextsToBeRemoved = append(m.BearerContextsToBeRemoved, i) } case ie.Recovery: m.Recovery = i case ie.SecondaryRATUsageDataReport: m.SecondaryRATUsageDataReport = append(m.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes ModifyAccessBearersRequest into bytes. func (m *ModifyAccessBearersRequest) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ModifyAccessBearersRequest into bytes. func (m *ModifyAccessBearersRequest) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.IndicationFlags; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SenderFTEIDC; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.DelayDownlinkPacketNotificationRequest; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.BearerContextsToBeModified { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.BearerContextsToBeRemoved { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.Recovery; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.SecondaryRATUsageDataReport { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseModifyAccessBearersRequest decodes given bytes as ModifyAccessBearersRequest. func ParseModifyAccessBearersRequest(b []byte) (*ModifyAccessBearersRequest, error) { m := &ModifyAccessBearersRequest{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as ModifyAccessBearersRequest. func (m *ModifyAccessBearersRequest) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Indication: m.IndicationFlags = i case ie.NodeType: m.SenderFTEIDC = i case ie.DelayValue: m.DelayDownlinkPacketNotificationRequest = i case ie.BearerContext: switch i.Instance() { case 0: m.BearerContextsToBeModified = append(m.BearerContextsToBeModified, i) case 1: m.BearerContextsToBeRemoved = append(m.BearerContextsToBeRemoved, i) } case ie.Recovery: m.Recovery = i case ie.SecondaryRATUsageDataReport: m.SecondaryRATUsageDataReport = append(m.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *ModifyAccessBearersRequest) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := m.SenderFTEIDC; ie != nil { l += ie.MarshalLen() } if ie := m.DelayDownlinkPacketNotificationRequest; ie != nil { l += ie.MarshalLen() } for _, ie := range m.BearerContextsToBeModified { l += ie.MarshalLen() } for _, ie := range m.BearerContextsToBeRemoved { l += ie.MarshalLen() } if ie := m.Recovery; ie != nil { l += ie.MarshalLen() } for _, ie := range m.SecondaryRATUsageDataReport { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *ModifyAccessBearersRequest) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *ModifyAccessBearersRequest) MessageTypeName() string { return "Modify Access Bearers Request" } // TEID returns the TEID in uint32. func (m *ModifyAccessBearersRequest) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/modify-access-bearers-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ModifyAccessBearersRequest into bytes. // // Deprecated: use ModifyAccessBearersRequest.Marshal instead. func (m *ModifyAccessBearersRequest) Serialize() ([]byte, error) { log.Println("ModifyAccessBearersRequest.Serialize is deprecated. use ModifyAccessBearersRequest.Marshal instead") return m.Marshal() } // SerializeTo serializes ModifyAccessBearersRequest into bytes given as b. // // Deprecated: use ModifyAccessBearersRequest.MarshalTo instead. func (m *ModifyAccessBearersRequest) SerializeTo(b []byte) error { log.Println("ModifyAccessBearersRequest.SerializeTo is deprecated. use ModifyAccessBearersRequest.MarshalTo instead") return m.MarshalTo(b) } // DecodeModifyAccessBearersRequest decodes bytes as ModifyAccessBearersRequest. // // Deprecated: use ParseModifyAccessBearersRequest instead. func DecodeModifyAccessBearersRequest(b []byte) (*ModifyAccessBearersRequest, error) { log.Println("DecodeModifyAccessBearersRequest is deprecated. use ParseModifyAccessBearersRequest instead") return ParseModifyAccessBearersRequest(b) } // DecodeFromBytes decodes bytes as ModifyAccessBearersRequest. // // Deprecated: use ModifyAccessBearersRequest.UnmarshalBinary instead. func (m *ModifyAccessBearersRequest) DecodeFromBytes(b []byte) error { log.Println("ModifyAccessBearersRequest.DecodeFromBytes is deprecated. use ModifyAccessBearersRequest.UnmarshalBinary instead") return m.UnmarshalBinary(b) } // Len returns the actual length of ModifyAccessBearersRequest. // // Deprecated: use ModifyAccessBearersRequest.MarshalLen instead. func (m *ModifyAccessBearersRequest) Len() int { log.Println("ModifyAccessBearersRequest.Len is deprecated. use ModifyAccessBearersRequest.MarshalLen instead") return m.MarshalLen() } ================================================ FILE: gtpv2/message/modify-access-bearers-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestModifyAccessBearersRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/NoIE", Structured: message.NewModifyAccessBearersRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ), Serialized: []byte{ // Header 0x48, 0xd3, 0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, }, }, { Description: "Normal/WithIndication", Structured: message.NewModifyAccessBearersRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIndicationFromOctets(0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40), ie.NewBearerContext( ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ie.NewBearerContext( ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ), Serialized: []byte{ // Header 0x48, 0xd3, 0x00, 0x3f, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Indication 0x4d, 0x00, 0x07, 0x00, 0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40, // BearerContext 1 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, // BearerContext 2 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseModifyAccessBearersRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/modify-access-bearers-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // ModifyAccessBearersResponse is a ModifyAccessBearersResponse Header and its IEs above. type ModifyAccessBearersResponse struct { *Header Cause *ie.IE BearerContextsModified []*ie.IE BearerContextsMarkedForRemoval []*ie.IE Recovery *ie.IE IndicationFlags *ie.IE SGWNodeLoadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewModifyAccessBearersResponse creates a new ModifyAccessBearersResponse. func NewModifyAccessBearersResponse(teid, seq uint32, ies ...*ie.IE) *ModifyAccessBearersResponse { m := &ModifyAccessBearersResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeModifyAccessBearersResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.BearerContext: switch i.Instance() { case 0: m.BearerContextsModified = append(m.BearerContextsModified, i) case 1: m.BearerContextsMarkedForRemoval = append(m.BearerContextsMarkedForRemoval, i) } case ie.Recovery: m.Recovery = i case ie.Indication: m.IndicationFlags = i case ie.LoadControlInformation: m.SGWNodeLoadControlInformation = i case ie.OverloadControlInformation: m.SGWOverloadControlInformation = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes ModifyAccessBearersResponse into bytes. func (m *ModifyAccessBearersResponse) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ModifyAccessBearersResponse into bytes. func (m *ModifyAccessBearersResponse) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.Cause; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.BearerContextsModified { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.BearerContextsMarkedForRemoval { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.Recovery; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.IndicationFlags; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseModifyAccessBearersResponse decodes given bytes as ModifyAccessBearersResponse. func ParseModifyAccessBearersResponse(b []byte) (*ModifyAccessBearersResponse, error) { m := &ModifyAccessBearersResponse{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as ModifyAccessBearersResponse. func (m *ModifyAccessBearersResponse) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.BearerContext: switch i.Instance() { case 0: m.BearerContextsModified = append(m.BearerContextsModified, i) case 1: m.BearerContextsMarkedForRemoval = append(m.BearerContextsMarkedForRemoval, i) } case ie.Recovery: m.Recovery = i case ie.Indication: m.IndicationFlags = i case ie.LoadControlInformation: m.SGWNodeLoadControlInformation = i case ie.OverloadControlInformation: m.SGWOverloadControlInformation = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *ModifyAccessBearersResponse) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.Cause; ie != nil { l += ie.MarshalLen() } for _, ie := range m.BearerContextsModified { l += ie.MarshalLen() } for _, ie := range m.BearerContextsMarkedForRemoval { l += ie.MarshalLen() } if ie := m.Recovery; ie != nil { l += ie.MarshalLen() } if ie := m.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := m.SGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *ModifyAccessBearersResponse) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *ModifyAccessBearersResponse) MessageTypeName() string { return "Modify Access Bearers Response" } // TEID returns the TEID in uint32. func (m *ModifyAccessBearersResponse) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/modify-access-bearers-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ModifyAccessBearersResponse into bytes. // // Deprecated: use ModifyAccessBearersResponse.Marshal instead. func (m *ModifyAccessBearersResponse) Serialize() ([]byte, error) { log.Println("ModifyAccessBearersResponse.Serialize is deprecated. use ModifyAccessBearersResponse.Marshal instead") return m.Marshal() } // SerializeTo serializes ModifyAccessBearersResponse into bytes given as b. // // Deprecated: use ModifyAccessBearersResponse.MarshalTo instead. func (m *ModifyAccessBearersResponse) SerializeTo(b []byte) error { log.Println("ModifyAccessBearersResponse.SerializeTo is deprecated. use ModifyAccessBearersResponse.MarshalTo instead") return m.MarshalTo(b) } // DecodeModifyAccessBearersResponse decodes bytes as ModifyAccessBearersResponse. // // Deprecated: use ParseModifyAccessBearersResponse instead. func DecodeModifyAccessBearersResponse(b []byte) (*ModifyAccessBearersResponse, error) { log.Println("DecodeModifyAccessBearersResponse is deprecated. use ParseModifyAccessBearersResponse instead") return ParseModifyAccessBearersResponse(b) } // DecodeFromBytes decodes bytes as ModifyAccessBearersResponse. // // Deprecated: use ModifyAccessBearersResponse.UnmarshalBinary instead. func (m *ModifyAccessBearersResponse) DecodeFromBytes(b []byte) error { log.Println("ModifyAccessBearersResponse.DecodeFromBytes is deprecated. use ModifyAccessBearersResponse.UnmarshalBinary instead") return m.UnmarshalBinary(b) } // Len returns the actual length of ModifyAccessBearersResponse. // // Deprecated: use ModifyAccessBearersResponse.MarshalLen instead. func (m *ModifyAccessBearersResponse) Len() int { log.Println("ModifyAccessBearersResponse.Len is deprecated. use ModifyAccessBearersResponse.MarshalLen instead") return m.MarshalLen() } ================================================ FILE: gtpv2/message/modify-access-bearers-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestModifyAccessBearersResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/CauseOnly", Structured: message.NewModifyAccessBearersResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewBearerContext( ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ie.NewBearerContext( ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ), Serialized: []byte{ // Header 0x48, 0xd4, 0x00, 0x3a, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // BearerContext 1 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, // BearerContext 2 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseModifyAccessBearersResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/modify-bearer-command.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // ModifyBearerCommand is a ModifyBearerCommand Header and its IEs above. type ModifyBearerCommand struct { *Header APNAMBR *ie.IE BearerContext *ie.IE MMESGSNOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE TWANePDGOverloadControlInformation *ie.IE SenderFTEIDC *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewModifyBearerCommand creates a new ModifyBearerCommand. func NewModifyBearerCommand(teid, seq uint32, ies ...*ie.IE) *ModifyBearerCommand { m := &ModifyBearerCommand{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeModifyBearerCommand, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.AggregateMaximumBitRate: m.APNAMBR = i case ie.BearerContext: m.BearerContext = i case ie.OverloadControlInformation: switch i.Instance() { case 0: m.MMESGSNOverloadControlInformation = i case 1: m.SGWOverloadControlInformation = i case 2: m.TWANePDGOverloadControlInformation = i } case ie.FullyQualifiedTEID: m.SenderFTEIDC = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes ModifyBearerCommand into bytes. func (m *ModifyBearerCommand) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ModifyBearerCommand into bytes. func (m *ModifyBearerCommand) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.APNAMBR; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.BearerContext; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.MMESGSNOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.TWANePDGOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SenderFTEIDC; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseModifyBearerCommand decodes given bytes as ModifyBearerCommand. func ParseModifyBearerCommand(b []byte) (*ModifyBearerCommand, error) { m := &ModifyBearerCommand{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as ModifyBearerCommand. func (m *ModifyBearerCommand) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.AggregateMaximumBitRate: m.APNAMBR = i case ie.BearerContext: m.BearerContext = i case ie.OverloadControlInformation: switch i.Instance() { case 0: m.MMESGSNOverloadControlInformation = i case 1: m.SGWOverloadControlInformation = i case 2: m.TWANePDGOverloadControlInformation = i } case ie.FullyQualifiedTEID: m.SenderFTEIDC = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *ModifyBearerCommand) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.APNAMBR; ie != nil { l += ie.MarshalLen() } if ie := m.BearerContext; ie != nil { l += ie.MarshalLen() } if ie := m.MMESGSNOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.TWANePDGOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.SenderFTEIDC; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *ModifyBearerCommand) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *ModifyBearerCommand) MessageTypeName() string { return "Modify Bearer Command" } // TEID returns the TEID in uint32. func (m *ModifyBearerCommand) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/modify-bearer-command_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "time" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestModifyBearerCommand(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewModifyBearerCommand( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewAggregateMaximumBitRate(0x11111111, 0x22222222), ie.NewBearerContext(ie.NewDelayValue(500*time.Millisecond), ie.NewDelayValue(100*time.Millisecond)), ), Serialized: []byte{ // Header 0x48, 0x40, 0x00, 0x22, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // APN-AMBR 0x48, 0x00, 0x08, 0x00, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, // BearerContext 0x5d, 0x00, 0x0a, 0x00, 0x5c, 0x00, 0x01, 0x00, 0x0a, 0x5c, 0x00, 0x01, 0x00, 0x02, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseModifyBearerCommand(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/modify-bearer-failure-indication.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // ModifyBearerFailureIndication is a ModifyBearerFailureIndication Header and its IEs above. type ModifyBearerFailureIndication struct { *Header Cause *ie.IE Recovery *ie.IE IndicationFlags *ie.IE PGWOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewModifyBearerFailureIndication creates a new ModifyBearerFailureIndication. func NewModifyBearerFailureIndication(teid, seq uint32, ies ...*ie.IE) *ModifyBearerFailureIndication { m := &ModifyBearerFailureIndication{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeModifyBearerFailureIndication, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.Recovery: m.Recovery = i case ie.Indication: m.IndicationFlags = i case ie.OverloadControlInformation: switch i.Instance() { case 0: m.PGWOverloadControlInformation = i case 1: m.SGWOverloadControlInformation = i } case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes ModifyBearerFailureIndication into bytes. func (m *ModifyBearerFailureIndication) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ModifyBearerFailureIndication into bytes. func (m *ModifyBearerFailureIndication) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.Cause; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.Recovery; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.IndicationFlags; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseModifyBearerFailureIndication decodes given bytes as ModifyBearerFailureIndication. func ParseModifyBearerFailureIndication(b []byte) (*ModifyBearerFailureIndication, error) { m := &ModifyBearerFailureIndication{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as ModifyBearerFailureIndication. func (m *ModifyBearerFailureIndication) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.Recovery: m.Recovery = i case ie.Indication: m.IndicationFlags = i case ie.OverloadControlInformation: switch i.Instance() { case 0: m.PGWOverloadControlInformation = i case 1: m.SGWOverloadControlInformation = i } case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *ModifyBearerFailureIndication) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.Cause; ie != nil { l += ie.MarshalLen() } if ie := m.Recovery; ie != nil { l += ie.MarshalLen() } if ie := m.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := m.PGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *ModifyBearerFailureIndication) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *ModifyBearerFailureIndication) MessageTypeName() string { return "Modify Bearer Failure Indication" } // TEID returns the TEID in uint32. func (m *ModifyBearerFailureIndication) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/modify-bearer-failure-indication_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestModifyBearerFailureIndication(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewModifyBearerFailureIndication( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ), Serialized: []byte{ // Header 0x48, 0x41, 0x00, 0x0e, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseModifyBearerFailureIndication(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/modify-bearer-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // ModifyBearerRequest is a ModifyBearerRequest Header and its IEs above. type ModifyBearerRequest struct { *Header MEI *ie.IE ULI *ie.IE ServingNetwork *ie.IE RATType *ie.IE IndicationFlags *ie.IE SenderFTEIDC *ie.IE AMBR *ie.IE DelayDownlinkPacketNotificationRequest *ie.IE BearerContextsToBeModified []*ie.IE BearerContextsToBeRemoved []*ie.IE Recovery *ie.IE UETimeZone *ie.IE MMEFQCSID *ie.IE SGWFQCSID *ie.IE UCI *ie.IE UELocalIPAddress *ie.IE UEUDPPort *ie.IE MMESGSNLDN *ie.IE SGWLDN *ie.IE HeNBLocalIPAddress *ie.IE HeNBUDPPort *ie.IE MMESGSNIdentifier *ie.IE CNOperatorSelectionEntity *ie.IE PresenceReportingAreaInformation []*ie.IE MMESGSNOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE EPDGOverloadControlInformation *ie.IE ServingPLMNRateControl *ie.IE MOExceptionDataCounter *ie.IE IMSI *ie.IE ULIForSGW *ie.IE WLANLocationInformation *ie.IE WLANLocationTimeStamp *ie.IE SecondaryRATUsageDataReport []*ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewModifyBearerRequest creates a new ModifyBearerRequest. func NewModifyBearerRequest(teid, seq uint32, ies ...*ie.IE) *ModifyBearerRequest { m := &ModifyBearerRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeModifyBearerRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.MobileEquipmentIdentity: m.MEI = i case ie.UserLocationInformation: switch i.Instance() { case 0: m.ULI = i case 1: m.ULIForSGW = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.ServingNetwork: m.ServingNetwork = i case ie.RATType: m.RATType = i case ie.Indication: m.IndicationFlags = i case ie.FullyQualifiedTEID: m.SenderFTEIDC = i case ie.AggregateMaximumBitRate: m.AMBR = i case ie.DelayValue: m.DelayDownlinkPacketNotificationRequest = i case ie.BearerContext: switch i.Instance() { case 0: m.BearerContextsToBeModified = append(m.BearerContextsToBeModified, i) case 1: m.BearerContextsToBeRemoved = append(m.BearerContextsToBeRemoved, i) default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.Recovery: m.Recovery = i case ie.UETimeZone: m.UETimeZone = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: m.MMEFQCSID = i case 1: m.SGWFQCSID = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.UserCSGInformation: m.UCI = i case ie.IPAddress: switch i.Instance() { case 0: m.UELocalIPAddress = i case 1: m.HeNBLocalIPAddress = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.PortNumber: switch i.Instance() { case 0: m.UEUDPPort = i case 1: m.HeNBUDPPort = i case 2: m.MMESGSNIdentifier = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.LocalDistinguishedName: switch i.Instance() { case 0: m.MMESGSNLDN = i case 1: m.SGWLDN = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.CNOperatorSelectionEntity: m.CNOperatorSelectionEntity = i case ie.PresenceReportingAreaInformation: m.PresenceReportingAreaInformation = append(m.PresenceReportingAreaInformation, i) case ie.OverloadControlInformation: switch i.Instance() { case 0: m.MMESGSNOverloadControlInformation = i case 1: m.SGWOverloadControlInformation = i case 2: m.EPDGOverloadControlInformation = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.ServingPLMNRateControl: m.ServingPLMNRateControl = i case ie.Counter: m.MOExceptionDataCounter = i case ie.IMSI: m.IMSI = i case ie.TWANIdentifier: m.WLANLocationInformation = i case ie.TWANIdentifierTimestamp: m.WLANLocationTimeStamp = i case ie.SecondaryRATUsageDataReport: m.SecondaryRATUsageDataReport = append(m.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes ModifyBearerRequest into bytes. func (m *ModifyBearerRequest) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ModifyBearerRequest into bytes. func (m *ModifyBearerRequest) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.MEI; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.ULI; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.ServingNetwork; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.RATType; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.IndicationFlags; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SenderFTEIDC; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.AMBR; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.DelayDownlinkPacketNotificationRequest; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.BearerContextsToBeModified { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.BearerContextsToBeRemoved { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.Recovery; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.UETimeZone; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.MMEFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.UCI; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.UELocalIPAddress; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.UEUDPPort; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.MMESGSNLDN; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWLDN; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.HeNBLocalIPAddress; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.HeNBUDPPort; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.MMESGSNIdentifier; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.CNOperatorSelectionEntity; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.PresenceReportingAreaInformation { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.MMESGSNOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.EPDGOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.ServingPLMNRateControl; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.MOExceptionDataCounter; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.IMSI; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.ULIForSGW; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.WLANLocationInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.WLANLocationTimeStamp; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.SecondaryRATUsageDataReport { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseModifyBearerRequest decodes given bytes as ModifyBearerRequest. func ParseModifyBearerRequest(b []byte) (*ModifyBearerRequest, error) { c := &ModifyBearerRequest{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as ModifyBearerRequest. func (m *ModifyBearerRequest) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.MobileEquipmentIdentity: m.MEI = i case ie.UserLocationInformation: switch i.Instance() { case 0: m.ULI = i case 1: m.ULIForSGW = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.ServingNetwork: m.ServingNetwork = i case ie.RATType: m.RATType = i case ie.Indication: m.IndicationFlags = i case ie.FullyQualifiedTEID: m.SenderFTEIDC = i case ie.AggregateMaximumBitRate: m.AMBR = i case ie.DelayValue: m.DelayDownlinkPacketNotificationRequest = i case ie.BearerContext: switch i.Instance() { case 0: m.BearerContextsToBeModified = append(m.BearerContextsToBeModified, i) case 1: m.BearerContextsToBeRemoved = append(m.BearerContextsToBeRemoved, i) default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.Recovery: m.Recovery = i case ie.UETimeZone: m.UETimeZone = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: m.MMEFQCSID = i case 1: m.SGWFQCSID = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.UserCSGInformation: m.UCI = i case ie.IPAddress: switch i.Instance() { case 0: m.UELocalIPAddress = i case 1: m.HeNBLocalIPAddress = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.PortNumber: switch i.Instance() { case 0: m.UEUDPPort = i case 1: m.HeNBUDPPort = i case 2: m.MMESGSNIdentifier = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.LocalDistinguishedName: switch i.Instance() { case 0: m.MMESGSNLDN = i case 1: m.SGWLDN = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.CNOperatorSelectionEntity: m.CNOperatorSelectionEntity = i case ie.PresenceReportingAreaInformation: m.PresenceReportingAreaInformation = append(m.PresenceReportingAreaInformation, i) case ie.OverloadControlInformation: switch i.Instance() { case 0: m.MMESGSNOverloadControlInformation = i case 1: m.SGWOverloadControlInformation = i case 2: m.EPDGOverloadControlInformation = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.ServingPLMNRateControl: m.ServingPLMNRateControl = i case ie.Counter: m.MOExceptionDataCounter = i case ie.IMSI: m.IMSI = i case ie.TWANIdentifier: m.WLANLocationInformation = i case ie.TWANIdentifierTimestamp: m.WLANLocationTimeStamp = i case ie.SecondaryRATUsageDataReport: m.SecondaryRATUsageDataReport = append(m.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *ModifyBearerRequest) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.MEI; ie != nil { l += ie.MarshalLen() } if ie := m.ULI; ie != nil { l += ie.MarshalLen() } if ie := m.ServingNetwork; ie != nil { l += ie.MarshalLen() } if ie := m.RATType; ie != nil { l += ie.MarshalLen() } if ie := m.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := m.SenderFTEIDC; ie != nil { l += ie.MarshalLen() } if ie := m.AMBR; ie != nil { l += ie.MarshalLen() } if ie := m.DelayDownlinkPacketNotificationRequest; ie != nil { l += ie.MarshalLen() } for _, ie := range m.BearerContextsToBeModified { l += ie.MarshalLen() } for _, ie := range m.BearerContextsToBeRemoved { l += ie.MarshalLen() } if ie := m.Recovery; ie != nil { l += ie.MarshalLen() } if ie := m.UETimeZone; ie != nil { l += ie.MarshalLen() } if ie := m.MMEFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.UCI; ie != nil { l += ie.MarshalLen() } if ie := m.UELocalIPAddress; ie != nil { l += ie.MarshalLen() } if ie := m.UEUDPPort; ie != nil { l += ie.MarshalLen() } if ie := m.MMESGSNLDN; ie != nil { l += ie.MarshalLen() } if ie := m.SGWLDN; ie != nil { l += ie.MarshalLen() } if ie := m.HeNBLocalIPAddress; ie != nil { l += ie.MarshalLen() } if ie := m.HeNBUDPPort; ie != nil { l += ie.MarshalLen() } if ie := m.MMESGSNIdentifier; ie != nil { l += ie.MarshalLen() } if ie := m.CNOperatorSelectionEntity; ie != nil { l += ie.MarshalLen() } for _, ie := range m.PresenceReportingAreaInformation { l += ie.MarshalLen() } if ie := m.MMESGSNOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.EPDGOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.ServingPLMNRateControl; ie != nil { l += ie.MarshalLen() } if ie := m.MOExceptionDataCounter; ie != nil { l += ie.MarshalLen() } if ie := m.IMSI; ie != nil { l += ie.MarshalLen() } if ie := m.ULIForSGW; ie != nil { l += ie.MarshalLen() } if ie := m.WLANLocationInformation; ie != nil { l += ie.MarshalLen() } if ie := m.WLANLocationTimeStamp; ie != nil { l += ie.MarshalLen() } for _, ie := range m.SecondaryRATUsageDataReport { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *ModifyBearerRequest) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *ModifyBearerRequest) MessageTypeName() string { return "Modify Bearer Request" } // TEID returns the TEID in uint32. func (m *ModifyBearerRequest) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/modify-bearer-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ModifyBearerRequest into bytes. // // Deprecated: use ModifyBearerRequest.Marshal instead. func (m *ModifyBearerRequest) Serialize() ([]byte, error) { log.Println("ModifyBearerRequest.Serialize is deprecated. use ModifyBearerRequest.Marshal instead") return m.Marshal() } // SerializeTo serializes ModifyBearerRequest into bytes given as b. // // Deprecated: use ModifyBearerRequest.MarshalTo instead. func (m *ModifyBearerRequest) SerializeTo(b []byte) error { log.Println("ModifyBearerRequest.SerializeTo is deprecated. use ModifyBearerRequest.MarshalTo instead") return m.MarshalTo(b) } // DecodeModifyBearerRequest decodes bytes as ModifyBearerRequest. // // Deprecated: use ParseModifyBearerRequest instead. func DecodeModifyBearerRequest(b []byte) (*ModifyBearerRequest, error) { log.Println("DecodeModifyBearerRequest is deprecated. use ParseModifyBearerRequest instead") return ParseModifyBearerRequest(b) } // DecodeFromBytes decodes bytes as ModifyBearerRequest. // // Deprecated: use ModifyBearerRequest.UnmarshalBinary instead. func (m *ModifyBearerRequest) DecodeFromBytes(b []byte) error { log.Println("ModifyBearerRequest.DecodeFromBytes is deprecated. use ModifyBearerRequest.UnmarshalBinary instead") return m.UnmarshalBinary(b) } // Len returns the actual length of ModifyBearerRequest. // // Deprecated: use ModifyBearerRequest.MarshalLen instead. func (m *ModifyBearerRequest) Len() int { log.Println("ModifyBearerRequest.Len is deprecated. use ModifyBearerRequest.MarshalLen instead") return m.MarshalLen() } ================================================ FILE: gtpv2/message/modify-bearer-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestModifyBearerRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/FromMMEtoSGW", Structured: message.NewModifyBearerRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewBearerContext( ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ie.NewBearerContext( ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ), Serialized: []byte{ // Header 0x48, 0x22, 0x00, 0x34, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // BearerContext 1 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, // BearerContext 2 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseModifyBearerRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/modify-bearer-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // ModifyBearerResponse is a ModifyBearerResponse Header and its IEs above. type ModifyBearerResponse struct { *Header Cause *ie.IE MSISDN *ie.IE LinkedEBI *ie.IE APNRestriction *ie.IE PCO *ie.IE BearerContextsModified []*ie.IE BearerContextsMarkedForRemoval []*ie.IE ChangeReportingAction *ie.IE CSGInformationReportingAction *ie.IE HeNBInformationReporting *ie.IE ChargingGatewayName *ie.IE ChargingGatewayAddress *ie.IE PGWFQCSID *ie.IE SGWFQCSID *ie.IE Recovery *ie.IE SGWLDN *ie.IE PGWLDN *ie.IE IndicationFlags *ie.IE PresenceReportingAreaAction []*ie.IE PGWNodeLoadControlInformation *ie.IE PGWAPNLoadControlInformation *ie.IE SGWNodeLoadControlInformation *ie.IE PGWOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE PDNConnectionChargingID *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewModifyBearerResponse creates a new ModifyBearerResponse. func NewModifyBearerResponse(teid, seq uint32, ies ...*ie.IE) *ModifyBearerResponse { m := &ModifyBearerResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeModifyBearerResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.MSISDN: m.MSISDN = i case ie.EPSBearerID: m.LinkedEBI = i case ie.APNRestriction: m.APNRestriction = i case ie.ProtocolConfigurationOptions: m.PCO = i case ie.BearerContext: switch i.Instance() { case 0: m.BearerContextsModified = append(m.BearerContextsModified, i) case 1: m.BearerContextsMarkedForRemoval = append(m.BearerContextsMarkedForRemoval, i) default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.ChangeReportingAction: m.ChangeReportingAction = i case ie.CSGInformationReportingAction: m.CSGInformationReportingAction = i case ie.HeNBInformationReporting: m.HeNBInformationReporting = i case ie.FullyQualifiedDomainName: m.ChargingGatewayName = i case ie.IPAddress: m.ChargingGatewayAddress = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: m.PGWFQCSID = i case 1: m.SGWFQCSID = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.Recovery: m.Recovery = i case ie.LocalDistinguishedName: switch i.Instance() { case 0: m.SGWLDN = i case 1: m.PGWLDN = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.Indication: m.IndicationFlags = i case ie.PresenceReportingAreaAction: m.PresenceReportingAreaAction = append(m.PresenceReportingAreaAction, i) case ie.LoadControlInformation: switch i.Instance() { case 0: m.PGWNodeLoadControlInformation = i case 1: m.PGWAPNLoadControlInformation = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: m.SGWNodeLoadControlInformation = i case 1: m.PGWOverloadControlInformation = i case 2: m.SGWOverloadControlInformation = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.ChargingID: m.PDNConnectionChargingID = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes ModifyBearerResponse into bytes. func (m *ModifyBearerResponse) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ModifyBearerResponse into bytes. func (m *ModifyBearerResponse) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.Cause; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.MSISDN; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.LinkedEBI; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.APNRestriction; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PCO; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.BearerContextsModified { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.BearerContextsMarkedForRemoval { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.ChangeReportingAction; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.CSGInformationReportingAction; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.HeNBInformationReporting; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.ChargingGatewayName; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.ChargingGatewayAddress; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PGWFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.Recovery; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWLDN; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PGWLDN; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.IndicationFlags; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.PresenceReportingAreaAction { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PGWAPNLoadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PDNConnectionChargingID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseModifyBearerResponse decodes given bytes as ModifyBearerResponse. func ParseModifyBearerResponse(b []byte) (*ModifyBearerResponse, error) { m := &ModifyBearerResponse{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as ModifyBearerResponse. func (m *ModifyBearerResponse) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.MSISDN: m.MSISDN = i case ie.EPSBearerID: m.LinkedEBI = i case ie.APNRestriction: m.APNRestriction = i case ie.ProtocolConfigurationOptions: m.PCO = i case ie.BearerContext: switch i.Instance() { case 0: m.BearerContextsModified = append(m.BearerContextsModified, i) case 1: m.BearerContextsMarkedForRemoval = append(m.BearerContextsMarkedForRemoval, i) default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.ChangeReportingAction: m.ChangeReportingAction = i case ie.CSGInformationReportingAction: m.CSGInformationReportingAction = i case ie.HeNBInformationReporting: m.HeNBInformationReporting = i case ie.FullyQualifiedDomainName: m.ChargingGatewayName = i case ie.IPAddress: m.ChargingGatewayAddress = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: m.PGWFQCSID = i case 1: m.SGWFQCSID = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.Recovery: m.Recovery = i case ie.LocalDistinguishedName: switch i.Instance() { case 0: m.SGWLDN = i case 1: m.PGWLDN = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.Indication: m.IndicationFlags = i case ie.PresenceReportingAreaAction: m.PresenceReportingAreaAction = append(m.PresenceReportingAreaAction, i) case ie.LoadControlInformation: switch i.Instance() { case 0: m.PGWNodeLoadControlInformation = i case 1: m.PGWAPNLoadControlInformation = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: m.SGWNodeLoadControlInformation = i case 1: m.PGWOverloadControlInformation = i case 2: m.SGWOverloadControlInformation = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.ChargingID: m.PDNConnectionChargingID = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *ModifyBearerResponse) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.Cause; ie != nil { l += ie.MarshalLen() } if ie := m.MSISDN; ie != nil { l += ie.MarshalLen() } if ie := m.LinkedEBI; ie != nil { l += ie.MarshalLen() } if ie := m.APNRestriction; ie != nil { l += ie.MarshalLen() } if ie := m.PCO; ie != nil { l += ie.MarshalLen() } for _, ie := range m.BearerContextsModified { l += ie.MarshalLen() } for _, ie := range m.BearerContextsMarkedForRemoval { l += ie.MarshalLen() } if ie := m.ChangeReportingAction; ie != nil { l += ie.MarshalLen() } if ie := m.CSGInformationReportingAction; ie != nil { l += ie.MarshalLen() } if ie := m.HeNBInformationReporting; ie != nil { l += ie.MarshalLen() } if ie := m.ChargingGatewayName; ie != nil { l += ie.MarshalLen() } if ie := m.ChargingGatewayAddress; ie != nil { l += ie.MarshalLen() } if ie := m.PGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.Recovery; ie != nil { l += ie.MarshalLen() } if ie := m.SGWLDN; ie != nil { l += ie.MarshalLen() } if ie := m.PGWLDN; ie != nil { l += ie.MarshalLen() } if ie := m.IndicationFlags; ie != nil { l += ie.MarshalLen() } for _, ie := range m.PresenceReportingAreaAction { l += ie.MarshalLen() } if ie := m.PGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.PGWAPNLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.SGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.PGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := m.PDNConnectionChargingID; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *ModifyBearerResponse) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *ModifyBearerResponse) MessageTypeName() string { return "Modify Bearer Response" } // TEID returns the TEID in uint32. func (m *ModifyBearerResponse) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/modify-bearer-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ModifyBearerResponse into bytes. // // Deprecated: use ModifyBearerResponse.Marshal instead. func (m *ModifyBearerResponse) Serialize() ([]byte, error) { log.Println("ModifyBearerResponse.Serialize is deprecated. use ModifyBearerResponse.Marshal instead") return m.Marshal() } // SerializeTo serializes ModifyBearerResponse into bytes given as b. // // Deprecated: use ModifyBearerResponse.MarshalTo instead. func (m *ModifyBearerResponse) SerializeTo(b []byte) error { log.Println("ModifyBearerResponse.SerializeTo is deprecated. use ModifyBearerResponse.MarshalTo instead") return m.MarshalTo(b) } // DecodeModifyBearerResponse decodes bytes as ModifyBearerResponse. // // Deprecated: use ParseModifyBearerResponse instead. func DecodeModifyBearerResponse(b []byte) (*ModifyBearerResponse, error) { log.Println("DecodeModifyBearerResponse is deprecated. use ParseModifyBearerResponse instead") return ParseModifyBearerResponse(b) } // DecodeFromBytes decodes bytes as ModifyBearerResponse. // // Deprecated: use ModifyBearerResponse.UnmarshalBinary instead. func (m *ModifyBearerResponse) DecodeFromBytes(b []byte) error { log.Println("ModifyBearerResponse.DecodeFromBytes is deprecated. use ModifyBearerResponse.UnmarshalBinary instead") return m.UnmarshalBinary(b) } // Len returns the actual length of ModifyBearerResponse. // // Deprecated: use ModifyBearerResponse.MarshalLen instead. func (m *ModifyBearerResponse) Len() int { log.Println("ModifyBearerResponse.Len is deprecated. use ModifyBearerResponse.MarshalLen instead") return m.MarshalLen() } ================================================ FILE: gtpv2/message/modify-bearer-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestModifyBearerResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/FromSGWtoMME", Structured: message.NewModifyBearerResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1USGWGTPU, 0xffffffff, "1.1.1.3", ""), ), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1USGWGTPU, 0xffffffff, "1.1.1.3", ""), ), ), Serialized: []byte{ // Header 0x48, 0x23, 0x00, 0x46, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // BearerContext 1 0x5d, 0x00, 0x18, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // FTEID 0x57, 0x00, 0x09, 0x00, 0x81, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x03, // BearerContext 2 0x5d, 0x00, 0x18, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // FTEID 0x57, 0x00, 0x09, 0x00, 0x81, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x03, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseModifyBearerResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/pgw-restart-notification-acknowledge.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // PGWRestartNotificationAcknowledge is a PGWRestartNotificationAcknowledge Header and its IEs above. type PGWRestartNotificationAcknowledge struct { *Header Cause *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewPGWRestartNotificationAcknowledge creates a new PGWRestartNotificationAcknowledge. func NewPGWRestartNotificationAcknowledge(teid, seq uint32, ies ...*ie.IE) *PGWRestartNotificationAcknowledge { m := &PGWRestartNotificationAcknowledge{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypePGWRestartNotificationAcknowledge, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes PGWRestartNotificationAcknowledge into bytes. func (m *PGWRestartNotificationAcknowledge) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes PGWRestartNotificationAcknowledge into bytes. func (m *PGWRestartNotificationAcknowledge) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.Cause; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParsePGWRestartNotificationAcknowledge decodes given bytes as PGWRestartNotificationAcknowledge. func ParsePGWRestartNotificationAcknowledge(b []byte) (*PGWRestartNotificationAcknowledge, error) { m := &PGWRestartNotificationAcknowledge{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as PGWRestartNotificationAcknowledge. func (m *PGWRestartNotificationAcknowledge) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *PGWRestartNotificationAcknowledge) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.Cause; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *PGWRestartNotificationAcknowledge) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *PGWRestartNotificationAcknowledge) MessageTypeName() string { return "PGW Restart Notification Acknowledge" } // TEID returns the TEID in uint32. func (m *PGWRestartNotificationAcknowledge) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/pgw-restart-notification-acknowledge_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestPGWRestartNotificationAcknowledge(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewPGWRestartNotificationAcknowledge( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CausePGWNotResponding, 0, 0, 0, nil), ), Serialized: []byte{ // Header 0x48, 0xb4, 0x00, 0x0e, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x0c, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParsePGWRestartNotificationAcknowledge(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/pgw-restart-notification.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // PGWRestartNotification is a PGWRestartNotification Header and its IEs above. type PGWRestartNotification struct { *Header PGWS5S8IPAddressForControlPlaneOrPMIP *ie.IE SGWS11S4IPAddressForControlPlane *ie.IE Cause *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewPGWRestartNotification creates a new PGWRestartNotification. func NewPGWRestartNotification(teid, seq uint32, ies ...*ie.IE) *PGWRestartNotification { m := &PGWRestartNotification{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypePGWRestartNotification, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IPAddress: switch i.Instance() { case 0: m.PGWS5S8IPAddressForControlPlaneOrPMIP = i case 1: m.SGWS11S4IPAddressForControlPlane = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.Cause: m.Cause = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes PGWRestartNotification into bytes. func (m *PGWRestartNotification) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes PGWRestartNotification into bytes. func (m *PGWRestartNotification) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.PGWS5S8IPAddressForControlPlaneOrPMIP; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWS11S4IPAddressForControlPlane; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.Cause; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParsePGWRestartNotification decodes given bytes as PGWRestartNotification. func ParsePGWRestartNotification(b []byte) (*PGWRestartNotification, error) { m := &PGWRestartNotification{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as PGWRestartNotification. func (m *PGWRestartNotification) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.IPAddress: switch i.Instance() { case 0: m.PGWS5S8IPAddressForControlPlaneOrPMIP = i case 1: m.SGWS11S4IPAddressForControlPlane = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.Cause: m.Cause = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *PGWRestartNotification) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.PGWS5S8IPAddressForControlPlaneOrPMIP; ie != nil { l += ie.MarshalLen() } if ie := m.SGWS11S4IPAddressForControlPlane; ie != nil { l += ie.MarshalLen() } if ie := m.Cause; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *PGWRestartNotification) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *PGWRestartNotification) MessageTypeName() string { return "PGW Restart Notification" } // TEID returns the TEID in uint32. func (m *PGWRestartNotification) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/pgw-restart-notification_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestPGWRestartNotification(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewPGWRestartNotification( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIPAddress("1.1.1.1"), ie.NewIPAddress("1.1.1.1").WithInstance(1), ie.NewCause(gtpv2.CausePGWNotResponding, 0, 0, 0, nil), ), Serialized: []byte{ // Header 0x48, 0xb3, 0x00, 0x1e, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // P-GW IP 0x4a, 0x00, 0x04, 0x00, 0x01, 0x01, 0x01, 0x01, // S-GW IP 0x4a, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, // Cause 0x02, 0x00, 0x02, 0x00, 0x0c, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParsePGWRestartNotification(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/release-access-bearers-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // ReleaseAccessBearersRequest is a ReleaseAccessBearersRequest Header and its IEs above. type ReleaseAccessBearersRequest struct { *Header ListOfRABs []*ie.IE OriginatingNode *ie.IE IndicationFlags *ie.IE SecondaryRATUsageDataReport []*ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewReleaseAccessBearersRequest creates a new ReleaseAccessBearersRequest. func NewReleaseAccessBearersRequest(teid, seq uint32, ies ...*ie.IE) *ReleaseAccessBearersRequest { r := &ReleaseAccessBearersRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeReleaseAccessBearersRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.EPSBearerID: r.ListOfRABs = append(r.ListOfRABs, i) case ie.NodeType: r.OriginatingNode = i case ie.Indication: r.IndicationFlags = i case ie.SecondaryRATUsageDataReport: r.SecondaryRATUsageDataReport = append(r.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: r.PrivateExtension = i default: r.AdditionalIEs = append(r.AdditionalIEs, i) } } r.SetLength() return r } // Marshal serializes ReleaseAccessBearersRequest into bytes. func (r *ReleaseAccessBearersRequest) Marshal() ([]byte, error) { b := make([]byte, r.MarshalLen()) if err := r.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ReleaseAccessBearersRequest into bytes. func (r *ReleaseAccessBearersRequest) MarshalTo(b []byte) error { if r.Header.Payload != nil { r.Header.Payload = nil } r.Header.Payload = make([]byte, r.MarshalLen()-r.Header.MarshalLen()) offset := 0 for _, ie := range r.ListOfRABs { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := r.OriginatingNode; ie != nil { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := r.IndicationFlags; ie != nil { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range r.SecondaryRATUsageDataReport { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := r.PrivateExtension; ie != nil { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range r.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(r.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } r.Header.SetLength() return r.Header.MarshalTo(b) } // ParseReleaseAccessBearersRequest decodes given bytes as ReleaseAccessBearersRequest. func ParseReleaseAccessBearersRequest(b []byte) (*ReleaseAccessBearersRequest, error) { r := &ReleaseAccessBearersRequest{} if err := r.UnmarshalBinary(b); err != nil { return nil, err } return r, nil } // UnmarshalBinary decodes given bytes as ReleaseAccessBearersRequest. func (r *ReleaseAccessBearersRequest) UnmarshalBinary(b []byte) error { var err error r.Header, err = ParseHeader(b) if err != nil { return err } if len(r.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(r.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.EPSBearerID: r.ListOfRABs = append(r.ListOfRABs, i) case ie.NodeType: r.OriginatingNode = i case ie.Indication: r.IndicationFlags = i case ie.SecondaryRATUsageDataReport: r.SecondaryRATUsageDataReport = append(r.SecondaryRATUsageDataReport, i) case ie.PrivateExtension: r.PrivateExtension = i default: r.AdditionalIEs = append(r.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (r *ReleaseAccessBearersRequest) MarshalLen() int { l := r.Header.MarshalLen() - len(r.Header.Payload) for _, ie := range r.ListOfRABs { l += ie.MarshalLen() } if ie := r.OriginatingNode; ie != nil { l += ie.MarshalLen() } if ie := r.IndicationFlags; ie != nil { l += ie.MarshalLen() } for _, ie := range r.SecondaryRATUsageDataReport { l += ie.MarshalLen() } if ie := r.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range r.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (r *ReleaseAccessBearersRequest) SetLength() { r.Header.Length = uint16(r.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (r *ReleaseAccessBearersRequest) MessageTypeName() string { return "Release Access Bearers Request" } // TEID returns the TEID in uint32. func (r *ReleaseAccessBearersRequest) TEID() uint32 { return r.Header.teid() } ================================================ FILE: gtpv2/message/release-access-bearers-req_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ReleaseAccessBearersRequest into bytes. // // Deprecated: use ReleaseAccessBearersRequest.Marshal instead. func (r *ReleaseAccessBearersRequest) Serialize() ([]byte, error) { log.Println("ReleaseAccessBearersRequest.Serialize is deprecated. use ReleaseAccessBearersRequest.Marshal instead") return r.Marshal() } // SerializeTo serializes ReleaseAccessBearersRequest into bytes given as b. // // Deprecated: use ReleaseAccessBearersRequest.MarshalTo instead. func (r *ReleaseAccessBearersRequest) SerializeTo(b []byte) error { log.Println("ReleaseAccessBearersRequest.SerializeTo is deprecated. use ReleaseAccessBearersRequest.MarshalTo instead") return r.MarshalTo(b) } // DecodeReleaseAccessBearersRequest decodes bytes as ReleaseAccessBearersRequest. // // Deprecated: use ParseReleaseAccessBearersRequest instead. func DecodeReleaseAccessBearersRequest(b []byte) (*ReleaseAccessBearersRequest, error) { log.Println("DecodeReleaseAccessBearersRequest is deprecated. use ParseReleaseAccessBearersRequest instead") return ParseReleaseAccessBearersRequest(b) } // DecodeFromBytes decodes bytes as ReleaseAccessBearersRequest. // // Deprecated: use ReleaseAccessBearersRequest.UnmarshalBinary instead. func (r *ReleaseAccessBearersRequest) DecodeFromBytes(b []byte) error { log.Println("ReleaseAccessBearersRequest.DecodeFromBytes is deprecated. use ReleaseAccessBearersRequest.UnmarshalBinary instead") return r.UnmarshalBinary(b) } // Len returns the actual length of ReleaseAccessBearersRequest. // // Deprecated: use ReleaseAccessBearersRequest.MarshalLen instead. func (r *ReleaseAccessBearersRequest) Len() int { log.Println("ReleaseAccessBearersRequest.Len is deprecated. use ReleaseAccessBearersRequest.MarshalLen instead") return r.MarshalLen() } ================================================ FILE: gtpv2/message/release-access-bearers-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestReleaseAccessBearersRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/NoIE", Structured: message.NewReleaseAccessBearersRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ), Serialized: []byte{ // Header 0x48, 0xaa, 0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, }, }, { Description: "Normal/WithIndication", Structured: message.NewReleaseAccessBearersRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIndicationFromOctets(0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40), ie.NewBearerContext( ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ie.NewBearerContext( ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ), Serialized: []byte{ // Header 0x48, 0xaa, 0x00, 0x3f, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Indication 0x4d, 0x00, 0x07, 0x00, 0xa1, 0x08, 0x15, 0x10, 0x88, 0x81, 0x40, // BearerContext 1 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, // BearerContext 2 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseReleaseAccessBearersRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/release-access-bearers-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // ReleaseAccessBearersResponse is a ReleaseAccessBearersResponse Header and its IEs above. type ReleaseAccessBearersResponse struct { *Header Cause *ie.IE Recovery *ie.IE IndicationFlags *ie.IE SGWNodeLoadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewReleaseAccessBearersResponse creates a new ReleaseAccessBearersResponse. func NewReleaseAccessBearersResponse(teid, seq uint32, ies ...*ie.IE) *ReleaseAccessBearersResponse { r := &ReleaseAccessBearersResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeReleaseAccessBearersResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: r.Cause = i case ie.Recovery: r.Recovery = i case ie.Indication: r.IndicationFlags = i case ie.LoadControlInformation: r.SGWNodeLoadControlInformation = i case ie.OverloadControlInformation: r.SGWOverloadControlInformation = i case ie.PrivateExtension: r.PrivateExtension = i default: r.AdditionalIEs = append(r.AdditionalIEs, i) } } r.SetLength() return r } // Marshal serializes ReleaseAccessBearersResponse into bytes. func (r *ReleaseAccessBearersResponse) Marshal() ([]byte, error) { b := make([]byte, r.MarshalLen()) if err := r.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ReleaseAccessBearersResponse into bytes. func (r *ReleaseAccessBearersResponse) MarshalTo(b []byte) error { if r.Header.Payload != nil { r.Header.Payload = nil } r.Header.Payload = make([]byte, r.MarshalLen()-r.Header.MarshalLen()) offset := 0 if ie := r.Cause; ie != nil { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := r.Recovery; ie != nil { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := r.IndicationFlags; ie != nil { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := r.SGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := r.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := r.PrivateExtension; ie != nil { if err := ie.MarshalTo(r.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range r.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(r.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } r.Header.SetLength() return r.Header.MarshalTo(b) } // ParseReleaseAccessBearersResponse decodes given bytes as ReleaseAccessBearersResponse. func ParseReleaseAccessBearersResponse(b []byte) (*ReleaseAccessBearersResponse, error) { r := &ReleaseAccessBearersResponse{} if err := r.UnmarshalBinary(b); err != nil { return nil, err } return r, nil } // UnmarshalBinary decodes given bytes as ReleaseAccessBearersResponse. func (r *ReleaseAccessBearersResponse) UnmarshalBinary(b []byte) error { var err error r.Header, err = ParseHeader(b) if err != nil { return err } if len(r.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(r.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: r.Cause = i case ie.Recovery: r.Recovery = i case ie.Indication: r.IndicationFlags = i case ie.LoadControlInformation: r.SGWNodeLoadControlInformation = i case ie.OverloadControlInformation: r.SGWOverloadControlInformation = i case ie.PrivateExtension: r.PrivateExtension = i default: r.AdditionalIEs = append(r.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (r *ReleaseAccessBearersResponse) MarshalLen() int { l := r.Header.MarshalLen() - len(r.Header.Payload) if ie := r.Cause; ie != nil { l += ie.MarshalLen() } if ie := r.Recovery; ie != nil { l += ie.MarshalLen() } if ie := r.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := r.SGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := r.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := r.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range r.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (r *ReleaseAccessBearersResponse) SetLength() { r.Header.Length = uint16(r.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (r *ReleaseAccessBearersResponse) MessageTypeName() string { return "Release Access Bearers Response" } // TEID returns the TEID in uint32. func (r *ReleaseAccessBearersResponse) TEID() uint32 { return r.Header.teid() } ================================================ FILE: gtpv2/message/release-access-bearers-res_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes ReleaseAccessBearersResponse into bytes. // // Deprecated: use ReleaseAccessBearersResponse.Marshal instead. func (r *ReleaseAccessBearersResponse) Serialize() ([]byte, error) { log.Println("ReleaseAccessBearersResponse.Serialize is deprecated. use ReleaseAccessBearersResponse.Marshal instead") return r.Marshal() } // SerializeTo serializes ReleaseAccessBearersResponse into bytes given as b. // // Deprecated: use ReleaseAccessBearersResponse.MarshalTo instead. func (r *ReleaseAccessBearersResponse) SerializeTo(b []byte) error { log.Println("ReleaseAccessBearersResponse.SerializeTo is deprecated. use ReleaseAccessBearersResponse.MarshalTo instead") return r.MarshalTo(b) } // DecodeReleaseAccessBearersResponse decodes bytes as ReleaseAccessBearersResponse. // // Deprecated: use ParseReleaseAccessBearersResponse instead. func DecodeReleaseAccessBearersResponse(b []byte) (*ReleaseAccessBearersResponse, error) { log.Println("DecodeReleaseAccessBearersResponse is deprecated. use ParseReleaseAccessBearersResponse instead") return ParseReleaseAccessBearersResponse(b) } // DecodeFromBytes decodes bytes as ReleaseAccessBearersResponse. // // Deprecated: use ReleaseAccessBearersResponse.UnmarshalBinary instead. func (r *ReleaseAccessBearersResponse) DecodeFromBytes(b []byte) error { log.Println("ReleaseAccessBearersResponse.DecodeFromBytes is deprecated. use ReleaseAccessBearersResponse.UnmarshalBinary instead") return r.UnmarshalBinary(b) } // Len returns the actual length of ReleaseAccessBearersResponse. // // Deprecated: use ReleaseAccessBearersResponse.MarshalLen instead. func (r *ReleaseAccessBearersResponse) Len() int { log.Println("ReleaseAccessBearersResponse.Len is deprecated. use ReleaseAccessBearersResponse.MarshalLen instead") return r.MarshalLen() } ================================================ FILE: gtpv2/message/release-access-bearers-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestReleaseAccessBearersResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/CauseOnly", Structured: message.NewReleaseAccessBearersResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ), Serialized: []byte{ // Header 0x48, 0xab, 0x00, 0x0e, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseReleaseAccessBearersResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/resume-acknowledge.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // ResumeAcknowledge is a ResumeAcknowledge Header and its Cause and Private Extension IEs. type ResumeAcknowledge struct { *Header Cause *ie.IE PrivateExtension *ie.IE } // NewResumeAcknowledge creates a new ResumeAcknowledge. func NewResumeAcknowledge(teid, seq uint32, ies ...*ie.IE) *ResumeAcknowledge { c := &ResumeAcknowledge{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeResumeAcknowledge, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.PrivateExtension: c.PrivateExtension = i } } c.SetLength() return c } // Marshal serializes ResumeAcknowledge into bytes. func (c *ResumeAcknowledge) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ResumeAcknowledge into bytes. func (c *ResumeAcknowledge) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseResumeAcknowledge decodes given bytes as ResumeAcknowledge. func ParseResumeAcknowledge(b []byte) (*ResumeAcknowledge, error) { c := &ResumeAcknowledge{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as ResumeAcknowledge. func (c *ResumeAcknowledge) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.PrivateExtension: c.PrivateExtension = i } } return nil } // MarshalLen returns the serial length in int. func (c *ResumeAcknowledge) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.Cause; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *ResumeAcknowledge) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *ResumeAcknowledge) MessageTypeName() string { return "Resume Acknowledge" } // TEID returns the TEID in uint32. func (c *ResumeAcknowledge) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/resume-acknowledge_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestResumeAcknowledge(t *testing.T) { cases := []testutils.TestCase{ { Description: "Just Cause", Structured: message.NewResumeAcknowledge( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ), Serialized: []byte{ // Header 0x48, 165, 0x00, 14, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, }, }, { Description: "With Private Extension", Structured: message.NewResumeAcknowledge( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewPrivateExtension(234, []byte{2, 3, 4, 5, 6}), ), Serialized: []byte{ // Header 0x48, 165, 0x00, 25, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // Private Extension 0xff, 0x00, 0x07, 0x00, 0x00, 234, 2, 3, 4, 5, 6, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseResumeAcknowledge(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/resume-notification.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // ResumeNotification is a ResumeNotification Header and its IEs. type ResumeNotification struct { *Header IMSI *ie.IE LinkedEPSBearerID *ie.IE OriginatingNode *ie.IE SenderFTEIDForControlPlane *ie.IE PrivateExtension *ie.IE } // NewResumeNotification creates a new ResumeNotification. func NewResumeNotification(teid, seq uint32, ies ...*ie.IE) *ResumeNotification { c := &ResumeNotification{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeResumeNotification, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.EPSBearerID: c.LinkedEPSBearerID = i case ie.NodeType: c.OriginatingNode = i case ie.FullyQualifiedTEID: c.SenderFTEIDForControlPlane = i case ie.PrivateExtension: c.PrivateExtension = i } } c.SetLength() return c } // Marshal serializes ResumeNotification into bytes. func (c *ResumeNotification) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes ResumeNotification into bytes. func (c *ResumeNotification) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.IMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.LinkedEPSBearerID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.OriginatingNode; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SenderFTEIDForControlPlane; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseResumeNotification decodes given bytes as ResumeNotification. func ParseResumeNotification(b []byte) (*ResumeNotification, error) { c := &ResumeNotification{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as ResumeNotification. func (c *ResumeNotification) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.EPSBearerID: c.LinkedEPSBearerID = i case ie.NodeType: c.OriginatingNode = i case ie.FullyQualifiedTEID: c.SenderFTEIDForControlPlane = i case ie.PrivateExtension: c.PrivateExtension = i } } return nil } // MarshalLen returns the serial length in int. func (c *ResumeNotification) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.IMSI; ie != nil { l += ie.MarshalLen() } if ie := c.LinkedEPSBearerID; ie != nil { l += ie.MarshalLen() } if ie := c.OriginatingNode; ie != nil { l += ie.MarshalLen() } if ie := c.SenderFTEIDForControlPlane; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *ResumeNotification) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *ResumeNotification) MessageTypeName() string { return "Resume Notification" } // TEID returns the TEID in uint32. func (c *ResumeNotification) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/resume-notification_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestResumeNotification(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewResumeNotification( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIMSI("123451234567890"), ie.NewEPSBearerID(5), ie.NewNodeType(gtpv2.NodeTypeMME), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS10MMEGTPC, 0xffffffff, "1.1.1.1", ""), ie.NewPrivateExtension(256, []byte{4, 5, 6, 7}), ), Serialized: []byte{ // Header 0x48, 164, 0x00, 53, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, // EPSBearerID 0x49, 0x00, 0x01, 0x00, 0x05, // NodeType 0x87, 0x00, 0x01, 0x00, 0x01, // FullyQualifiedTEID 0x57, 0x00, 0x09, 0x00, 0x8c, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, // PrivateExtension 0xff, 0x00, 0x06, 0x00, 1, 0, 4, 5, 6, 7, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseResumeNotification(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/stop-paging-indication.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "github.com/wmnsk/go-gtp/gtpv2/ie" // StopPagingIndication is a StopPagingIndication Header and its IEs above. type StopPagingIndication struct { *Header IMSI *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewStopPagingIndication creates a new StopPagingIndication. func NewStopPagingIndication(teid, seq uint32, ies ...*ie.IE) *StopPagingIndication { s := &StopPagingIndication{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeStopPagingIndication, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: s.IMSI = i case ie.PrivateExtension: s.PrivateExtension = i default: s.AdditionalIEs = append(s.AdditionalIEs, i) } } s.SetLength() return s } // Marshal serializes StopPagingIndication into bytes. func (s *StopPagingIndication) Marshal() ([]byte, error) { b := make([]byte, s.MarshalLen()) if err := s.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes StopPagingIndication into bytes. func (s *StopPagingIndication) MarshalTo(b []byte) error { if s.Header.Payload != nil { s.Header.Payload = nil } s.Header.Payload = make([]byte, s.MarshalLen()-s.Header.MarshalLen()) offset := 0 if ie := s.IMSI; ie != nil { if err := ie.MarshalTo(s.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := s.PrivateExtension; ie != nil { if err := ie.MarshalTo(s.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range s.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(s.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } s.Header.SetLength() return s.Header.MarshalTo(b) } // ParseStopPagingIndication decodes given bytes as StopPagingIndication. func ParseStopPagingIndication(b []byte) (*StopPagingIndication, error) { s := &StopPagingIndication{} if err := s.UnmarshalBinary(b); err != nil { return nil, err } return s, nil } // UnmarshalBinary decodes given bytes as StopPagingIndication. func (s *StopPagingIndication) UnmarshalBinary(b []byte) error { var err error s.Header, err = ParseHeader(b) if err != nil { return err } if len(s.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(s.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.IMSI: s.IMSI = i case ie.PrivateExtension: s.PrivateExtension = i default: s.AdditionalIEs = append(s.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (s *StopPagingIndication) MarshalLen() int { l := s.Header.MarshalLen() - len(s.Header.Payload) if ie := s.IMSI; ie != nil { l += ie.MarshalLen() } if ie := s.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range s.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (s *StopPagingIndication) SetLength() { s.Header.Length = uint16(s.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (s *StopPagingIndication) MessageTypeName() string { return "Stop Paging Indication" } // TEID returns the TEID in uint32. func (s *StopPagingIndication) TEID() uint32 { return s.Header.teid() } ================================================ FILE: gtpv2/message/stop-paging-indication_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes StopPagingIndication into bytes. // // Deprecated: use StopPagingIndication.Marshal instead. func (s *StopPagingIndication) Serialize() ([]byte, error) { log.Println("StopPagingIndication.Serialize is deprecated. use StopPagingIndication.Marshal instead") return s.Marshal() } // SerializeTo serializes StopPagingIndication into bytes given as b. // // Deprecated: use StopPagingIndication.MarshalTo instead. func (s *StopPagingIndication) SerializeTo(b []byte) error { log.Println("StopPagingIndication.SerializeTo is deprecated. use StopPagingIndication.MarshalTo instead") return s.MarshalTo(b) } // DecodeStopPagingIndication decodes bytes as StopPagingIndication. // // Deprecated: use ParseStopPagingIndication instead. func DecodeStopPagingIndication(b []byte) (*StopPagingIndication, error) { log.Println("DecodeStopPagingIndication is deprecated. use ParseStopPagingIndication instead") return ParseStopPagingIndication(b) } // DecodeFromBytes decodes bytes as StopPagingIndication. // // Deprecated: use StopPagingIndication.UnmarshalBinary instead. func (s *StopPagingIndication) DecodeFromBytes(b []byte) error { log.Println("StopPagingIndication.DecodeFromBytes is deprecated. use StopPagingIndication.UnmarshalBinary instead") return s.UnmarshalBinary(b) } // Len returns the actual length of StopPagingIndication. // // Deprecated: use StopPagingIndication.MarshalLen instead. func (s *StopPagingIndication) Len() int { log.Println("StopPagingIndication.Len is deprecated. use StopPagingIndication.MarshalLen instead") return s.MarshalLen() } ================================================ FILE: gtpv2/message/stop-paging-indication_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestStopPagingIndication(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/CauseOnly", Structured: message.NewStopPagingIndication( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIMSI("123451234567890"), ), Serialized: []byte{ // Header 0x48, 0x49, 0x00, 0x14, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseStopPagingIndication(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/suspend-acknowledge.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // SuspendAcknowledge is a SuspendAcknowledge Header and its IEs above. type SuspendAcknowledge struct { *Header Cause *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewSuspendAcknowledge creates a new SuspendAcknowledge. func NewSuspendAcknowledge(teid, seq uint32, ies ...*ie.IE) *SuspendAcknowledge { c := &SuspendAcknowledge{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeSuspendAcknowledge, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes SuspendAcknowledge into bytes. func (c *SuspendAcknowledge) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes SuspendAcknowledge into bytes. func (c *SuspendAcknowledge) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseSuspendAcknowledge decodes given bytes as SuspendAcknowledge. func ParseSuspendAcknowledge(b []byte) (*SuspendAcknowledge, error) { c := &SuspendAcknowledge{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as SuspendAcknowledge. func (c *SuspendAcknowledge) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *SuspendAcknowledge) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.Cause; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *SuspendAcknowledge) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *SuspendAcknowledge) MessageTypeName() string { return "Suspend Acknowledge" } // TEID returns the TEID in uint32. func (c *SuspendAcknowledge) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/suspend-acknowledge_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestSuspendAcknowledge(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewSuspendAcknowledge( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ), Serialized: []byte{ // Header 0x48, 163, 0x00, 14, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, }, }, { Description: "With Private Extension", Structured: message.NewSuspendAcknowledge( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewPrivateExtension(123, []byte{1, 2, 3, 4}), ), Serialized: []byte{ // Header 0x48, 163, 0x00, 24, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // Private Extension 0xff, 0x00, 0x06, 0x00, 0x00, 123, 1, 2, 3, 4, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseSuspendAcknowledge(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/suspend-notification.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // SuspendNotification is a SuspendNotification Header and its IEs above. type SuspendNotification struct { *Header IMSI *ie.IE RAI *ie.IE LinkedEBI *ie.IE PTMSI *ie.IE OriginatingNode *ie.IE AddressForControlPlane *ie.IE UDPSourcePortNumber *ie.IE HopCounter *ie.IE SenderFTEIDC *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewSuspendNotification creates a new SuspendNotification. func NewSuspendNotification(teid, seq uint32, ies ...*ie.IE) *SuspendNotification { c := &SuspendNotification{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeSuspendNotification, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.UserLocationInformation: c.RAI = i case ie.EPSBearerID: c.LinkedEBI = i case ie.PacketTMSI: c.PTMSI = i case ie.NodeType: c.OriginatingNode = i case ie.IPAddress: c.AddressForControlPlane = i case ie.PortNumber: c.UDPSourcePortNumber = i case ie.HopCounter: c.HopCounter = i case ie.FullyQualifiedTEID: c.SenderFTEIDC = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes SuspendNotification into bytes. func (c *SuspendNotification) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes SuspendNotification into bytes. func (c *SuspendNotification) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.IMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.RAI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.LinkedEBI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PTMSI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.OriginatingNode; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.AddressForControlPlane; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UDPSourcePortNumber; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.HopCounter; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SenderFTEIDC; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseSuspendNotification decodes given bytes as SuspendNotification. func ParseSuspendNotification(b []byte) (*SuspendNotification, error) { c := &SuspendNotification{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as SuspendNotification. func (c *SuspendNotification) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.IMSI: c.IMSI = i case ie.UserLocationInformation: c.RAI = i case ie.PacketTMSI: c.PTMSI = i case ie.EPSBearerID: c.LinkedEBI = i case ie.NodeType: c.OriginatingNode = i case ie.IPAddress: c.AddressForControlPlane = i case ie.PortNumber: c.UDPSourcePortNumber = i case ie.HopCounter: c.HopCounter = i case ie.FullyQualifiedTEID: c.SenderFTEIDC = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *SuspendNotification) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.IMSI; ie != nil { l += ie.MarshalLen() } if ie := c.RAI; ie != nil { l += ie.MarshalLen() } if ie := c.PTMSI; ie != nil { l += ie.MarshalLen() } if ie := c.LinkedEBI; ie != nil { l += ie.MarshalLen() } if ie := c.OriginatingNode; ie != nil { l += ie.MarshalLen() } if ie := c.AddressForControlPlane; ie != nil { l += ie.MarshalLen() } if ie := c.UDPSourcePortNumber; ie != nil { l += ie.MarshalLen() } if ie := c.HopCounter; ie != nil { l += ie.MarshalLen() } if ie := c.SenderFTEIDC; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *SuspendNotification) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *SuspendNotification) MessageTypeName() string { return "Suspend Notification" } // TEID returns the TEID in uint32. func (c *SuspendNotification) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/suspend-notification_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestSuspendNotification(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewSuspendNotification( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewIMSI("123451234567890"), ie.NewUserLocationInformationStruct( nil, nil, nil, ie.NewTAI("123", "45", 0x0001), ie.NewECGI("123", "45", 0x00000101), nil, nil, nil, ), ie.NewEPSBearerID(5), ie.NewPacketTMSI(0xdeadbeef), ie.NewNodeType(gtpv2.NodeTypeMME), ie.NewIPAddress("1.1.1.1"), ie.NewPortNumber(2123), ie.NewHopCounter(1), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS10MMEGTPC, 0xffffffff, "1.1.1.1", ""), ie.NewPrivateExtension(123, []byte{1, 2, 3, 4}), ), Serialized: []byte{ // Header 0x48, 162, 0x00, 97, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // IMSI 0x01, 0x00, 0x08, 0x00, 0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0, // UserLocationInformation 0x56, 0x00, 0x0d, 0x00, 0x18, 0x21, 0xf3, 0x54, 0x00, 0x01, 0x21, 0xf3, 0x54, 0x00, 0x00, 0x01, 0x01, // EPSBearerID 0x49, 0x00, 0x01, 0x00, 0x05, // PacketTMSI 0x6f, 0x00, 0x04, 0x00, 0xde, 0xad, 0xbe, 0xef, // NodeType 0x87, 0x00, 0x01, 0x00, 0x01, // IPAddress 0x4a, 0x00, 0x04, 0x00, 0x01, 0x01, 0x01, 0x01, // PortNumber 0x7e, 0x00, 0x02, 0x00, 0x08, 0x4b, // HopCounter 0x71, 0x00, 0x01, 0x00, 0x01, // FullyQualifiedTEID 0x57, 0x00, 0x09, 0x00, 0x8c, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, // PrivateExtension 0xff, 0x00, 0x06, 0x00, 0x00, 123, 1, 2, 3, 4, }, }, { Description: "Erlang binary", Structured: message.NewSuspendNotification( 1024, 127, ie.NewIPAddress("1.2.3.4"), ie.NewPortNumber(2345), ), Serialized: []byte{ 0x48, 0xa2, 0x00, 0x16, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x4a, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04, 0x7e, 0x00, 0x02, 0x00, 0x09, 0x29, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseSuspendNotification(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/update-bearer-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // UpdateBearerRequest is a UpdateBearerRequest Header and its IEs above. type UpdateBearerRequest struct { *Header BearerContexts []*ie.IE PTI *ie.IE PCO *ie.IE APNAMBR *ie.IE ChangeReportingAction *ie.IE CSGInformationReportingAction *ie.IE HeNBInformationReporting *ie.IE IndicationFlags *ie.IE PGWFQCSID *ie.IE SGWFQCSID *ie.IE PresenceReportingAction []*ie.IE PGWNodeLoadControlInformation *ie.IE PGWAPNLoadControlInformation *ie.IE SGWNodeLoadControlInformation *ie.IE PGWOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE NBIFOMContainer *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewUpdateBearerRequest creates a new UpdateBearerRequest. func NewUpdateBearerRequest(teid, seq uint32, ies ...*ie.IE) *UpdateBearerRequest { c := &UpdateBearerRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeUpdateBearerRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.ProcedureTransactionID: c.PTI = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.AggregateMaximumBitRate: c.APNAMBR = i case ie.ChangeReportingAction: c.ChangeReportingAction = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.HeNBInformationReporting: c.HeNBInformationReporting = i case ie.Indication: c.IndicationFlags = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.PGWFQCSID = i case 1: c.SGWFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PresenceReportingAreaAction: c.PresenceReportingAction = append(c.PresenceReportingAction, i) case ie.LoadControlInformation: switch i.Instance() { case 0: c.PGWNodeLoadControlInformation = i case 1: c.PGWAPNLoadControlInformation = i case 2: c.SGWNodeLoadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.PGWOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FContainer: c.NBIFOMContainer = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes UpdateBearerRequest into bytes. func (c *UpdateBearerRequest) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes UpdateBearerRequest into bytes. func (c *UpdateBearerRequest) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 for _, ie := range c.BearerContexts { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PTI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.APNAMBR; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ChangeReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.HeNBInformationReporting; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.PresenceReportingAction { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWAPNLoadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWNodeLoadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseUpdateBearerRequest decodes given bytes as UpdateBearerRequest. func ParseUpdateBearerRequest(b []byte) (*UpdateBearerRequest, error) { c := &UpdateBearerRequest{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as UpdateBearerRequest. func (c *UpdateBearerRequest) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { switch i.Type { case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.ProcedureTransactionID: c.PTI = i case ie.ProtocolConfigurationOptions: c.PCO = i case ie.AggregateMaximumBitRate: c.APNAMBR = i case ie.ChangeReportingAction: c.ChangeReportingAction = i case ie.CSGInformationReportingAction: c.CSGInformationReportingAction = i case ie.HeNBInformationReporting: c.HeNBInformationReporting = i case ie.Indication: c.IndicationFlags = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.PGWFQCSID = i case 1: c.SGWFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PresenceReportingAreaAction: c.PresenceReportingAction = append(c.PresenceReportingAction, i) case ie.LoadControlInformation: switch i.Instance() { case 0: c.PGWNodeLoadControlInformation = i case 1: c.PGWAPNLoadControlInformation = i case 2: c.SGWNodeLoadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.PGWOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FContainer: c.NBIFOMContainer = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *UpdateBearerRequest) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) for _, ie := range c.BearerContexts { l += ie.MarshalLen() } if ie := c.PTI; ie != nil { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } if ie := c.APNAMBR; ie != nil { l += ie.MarshalLen() } if ie := c.ChangeReportingAction; ie != nil { l += ie.MarshalLen() } if ie := c.CSGInformationReportingAction; ie != nil { l += ie.MarshalLen() } if ie := c.HeNBInformationReporting; ie != nil { l += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := c.PGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { l += ie.MarshalLen() } for _, ie := range c.PresenceReportingAction { l += ie.MarshalLen() } if ie := c.PGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.PGWAPNLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.SGWNodeLoadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.PGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *UpdateBearerRequest) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *UpdateBearerRequest) MessageTypeName() string { return "Update Bearer Request" } // TEID returns the TEID in uint32. func (c *UpdateBearerRequest) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/update-bearer-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestUpdateBearerRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewUpdateBearerRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewBearerContext( ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ie.NewBearerContext( ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, 0xffffffff, "1.1.1.4", ""), ), ), Serialized: []byte{ // Header 0x48, 0x61, 0x00, 0x34, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // BearerContext 1 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, // BearerContext 2 0x5d, 0x00, 0x12, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // FTEID 0x57, 0x00, 0x09, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseUpdateBearerRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/update-bearer-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // UpdateBearerResponse is a UpdateBearerResponse Header and its IEs above. type UpdateBearerResponse struct { *Header Cause *ie.IE BearerContexts []*ie.IE PCO *ie.IE Recovery *ie.IE MMEFQCSID *ie.IE SGWFQCSID *ie.IE EPDGFQCSID *ie.IE TWANFQCSID *ie.IE IndicationFlags *ie.IE UETimeZone *ie.IE ULI *ie.IE TWANIdentifier *ie.IE MMESGSNOverloadControlInformation *ie.IE SGWOverloadControlInformation *ie.IE PresenceReportingAreaAction []*ie.IE MMESGSNIdentifier *ie.IE TWANePDGOverloadControlInformation *ie.IE WLANLocationInformation *ie.IE WLANLocationTimeStamp *ie.IE UELocalIPAddress *ie.IE UEUDPPort *ie.IE NBIFOMContainer *ie.IE UETCPPort *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewUpdateBearerRequest creates a new UpdateBearerRequest. func NewUpdateBearerResponse(teid, seq uint32, ies ...*ie.IE) *UpdateBearerResponse { c := &UpdateBearerResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeUpdateBearerResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: c.Cause = i case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.ProtocolConfigurationOptions: c.PCO = i case ie.Recovery: c.Recovery = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.MMEFQCSID = i case 1: c.SGWFQCSID = i case 2: c.EPDGFQCSID = i case 3: c.TWANFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.Indication: c.IndicationFlags = i case ie.UETimeZone: c.UETimeZone = i case ie.UserLocationInformation: c.ULI = i case ie.TWANIdentifier: switch i.Instance() { case 0: c.TWANIdentifier = i case 1: c.WLANLocationInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.MMESGSNOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i case 2: c.TWANePDGOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PresenceReportingAreaAction: c.PresenceReportingAreaAction = append(c.PresenceReportingAreaAction, i) case ie.IPAddress: switch i.Instance() { case 0: c.MMESGSNIdentifier = i case 1: c.UELocalIPAddress = i } case ie.TWANIdentifierTimestamp: switch i.Instance() { case 1: c.WLANLocationTimeStamp = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PortNumber: switch i.Instance() { case 0: c.UEUDPPort = i case 1: c.UETCPPort = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FContainer: c.NBIFOMContainer = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } c.SetLength() return c } // Marshal serializes UpdateBearerResponse into bytes. func (c *UpdateBearerResponse) Marshal() ([]byte, error) { b := make([]byte, c.MarshalLen()) if err := c.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes UpdateBearerResponse into bytes. func (c *UpdateBearerResponse) MarshalTo(b []byte) error { if c.Header.Payload != nil { c.Header.Payload = nil } c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen()) offset := 0 if ie := c.Cause; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.BearerContexts { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PCO; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.Recovery; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMEFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.EPDGFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANFQCSID; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UETimeZone; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.ULI; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANIdentifier; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMESGSNOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaAction { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.MMESGSNIdentifier; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.TWANePDGOverloadControlInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.WLANLocationInformation; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.WLANLocationTimeStamp; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UELocalIPAddress; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UEUDPPort; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.UETCPPort; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { if err := ie.MarshalTo(c.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(c.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } c.Header.SetLength() return c.Header.MarshalTo(b) } // ParseUpdateBearerResponse decodes given bytes as UpdateBearerResponse. func ParseUpdateBearerResponse(b []byte) (*UpdateBearerResponse, error) { c := &UpdateBearerResponse{} if err := c.UnmarshalBinary(b); err != nil { return nil, err } return c, nil } // UnmarshalBinary decodes given bytes as UpdateBearerResponse. func (c *UpdateBearerResponse) UnmarshalBinary(b []byte) error { var err error c.Header, err = ParseHeader(b) if err != nil { return err } if len(c.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(c.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { switch i.Type { case ie.Cause: c.Cause = i case ie.BearerContext: c.BearerContexts = append(c.BearerContexts, i) case ie.ProtocolConfigurationOptions: c.PCO = i case ie.Recovery: c.Recovery = i case ie.FullyQualifiedCSID: switch i.Instance() { case 0: c.MMEFQCSID = i case 1: c.SGWFQCSID = i case 2: c.EPDGFQCSID = i case 3: c.TWANFQCSID = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.Indication: c.IndicationFlags = i case ie.UETimeZone: c.UETimeZone = i case ie.UserLocationInformation: c.ULI = i case ie.TWANIdentifier: switch i.Instance() { case 0: c.TWANIdentifier = i case 1: c.WLANLocationInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.OverloadControlInformation: switch i.Instance() { case 0: c.MMESGSNOverloadControlInformation = i case 1: c.SGWOverloadControlInformation = i case 2: c.TWANePDGOverloadControlInformation = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PresenceReportingAreaAction: c.PresenceReportingAreaAction = append(c.PresenceReportingAreaAction, i) case ie.IPAddress: switch i.Instance() { case 0: c.MMESGSNIdentifier = i case 1: c.UELocalIPAddress = i } case ie.TWANIdentifierTimestamp: switch i.Instance() { case 1: c.WLANLocationTimeStamp = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.PortNumber: switch i.Instance() { case 0: c.UEUDPPort = i case 1: c.UETCPPort = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } case ie.FContainer: c.NBIFOMContainer = i case ie.PrivateExtension: c.PrivateExtension = i default: c.AdditionalIEs = append(c.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (c *UpdateBearerResponse) MarshalLen() int { l := c.Header.MarshalLen() - len(c.Header.Payload) if ie := c.Cause; ie != nil { l += ie.MarshalLen() } for _, ie := range c.BearerContexts { l += ie.MarshalLen() } if ie := c.PCO; ie != nil { l += ie.MarshalLen() } if ie := c.Recovery; ie != nil { l += ie.MarshalLen() } if ie := c.MMEFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.EPDGFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.TWANFQCSID; ie != nil { l += ie.MarshalLen() } if ie := c.IndicationFlags; ie != nil { l += ie.MarshalLen() } if ie := c.UETimeZone; ie != nil { l += ie.MarshalLen() } if ie := c.ULI; ie != nil { l += ie.MarshalLen() } if ie := c.TWANIdentifier; ie != nil { l += ie.MarshalLen() } if ie := c.MMESGSNOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.SGWOverloadControlInformation; ie != nil { l += ie.MarshalLen() } for _, ie := range c.PresenceReportingAreaAction { l += ie.MarshalLen() } if ie := c.MMESGSNIdentifier; ie != nil { l += ie.MarshalLen() } if ie := c.TWANePDGOverloadControlInformation; ie != nil { l += ie.MarshalLen() } if ie := c.WLANLocationInformation; ie != nil { l += ie.MarshalLen() } if ie := c.WLANLocationTimeStamp; ie != nil { l += ie.MarshalLen() } if ie := c.UELocalIPAddress; ie != nil { l += ie.MarshalLen() } if ie := c.UEUDPPort; ie != nil { l += ie.MarshalLen() } if ie := c.NBIFOMContainer; ie != nil { l += ie.MarshalLen() } if ie := c.UETCPPort; ie != nil { l += ie.MarshalLen() } if ie := c.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range c.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (c *UpdateBearerResponse) SetLength() { c.Header.Length = uint16(c.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (c *UpdateBearerResponse) MessageTypeName() string { return "Update Bearer Response" } // TEID returns the TEID in uint32. func (c *UpdateBearerResponse) TEID() uint32 { return c.Header.teid() } ================================================ FILE: gtpv2/message/update-bearer-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestUpdateBearerResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewUpdateBearerResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(0x05), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1USGWGTPU, 0xffffffff, "1.1.1.3", ""), ), ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(0x06), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1USGWGTPU, 0xffffffff, "1.1.1.3", ""), ), ), Serialized: []byte{ // Header 0x48, 0x62, 0x00, 0x46, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // BearerContext 1 0x5d, 0x00, 0x18, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x05, // FTEID 0x57, 0x00, 0x09, 0x00, 0x81, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x03, // BearerContext 2 0x5d, 0x00, 0x18, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // EBI 0x49, 0x00, 0x01, 0x00, 0x06, // FTEID 0x57, 0x00, 0x09, 0x00, 0x81, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x03, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseUpdateBearerResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/update-pdn-connection-set-req.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // UpdatePDNConnectionSetRequest is a UpdatePDNConnectionSetRequest Header and its IEs above. type UpdatePDNConnectionSetRequest struct { *Header MMEFQCSID *ie.IE SGWFQCSID *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewUpdatePDNConnectionSetRequest creates a new UpdatePDNConnectionSetRequest. func NewUpdatePDNConnectionSetRequest(teid, seq uint32, ies ...*ie.IE) *UpdatePDNConnectionSetRequest { m := &UpdatePDNConnectionSetRequest{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeUpdatePDNConnectionSetRequest, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.FullyQualifiedCSID: switch i.Instance() { case 0: m.MMEFQCSID = i case 1: m.SGWFQCSID = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal serializes UpdatePDNConnectionSetRequest into bytes. func (m *UpdatePDNConnectionSetRequest) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes UpdatePDNConnectionSetRequest into bytes. func (m *UpdatePDNConnectionSetRequest) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.MMEFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.SGWFQCSID; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseUpdatePDNConnectionSetRequest decodes given bytes as UpdatePDNConnectionSetRequest. func ParseUpdatePDNConnectionSetRequest(b []byte) (*UpdatePDNConnectionSetRequest, error) { m := &UpdatePDNConnectionSetRequest{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given bytes as UpdatePDNConnectionSetRequest. func (m *UpdatePDNConnectionSetRequest) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.FullyQualifiedCSID: switch i.Instance() { case 0: m.MMEFQCSID = i case 1: m.SGWFQCSID = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length in int. func (m *UpdatePDNConnectionSetRequest) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.MMEFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.SGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *UpdatePDNConnectionSetRequest) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *UpdatePDNConnectionSetRequest) MessageTypeName() string { return "Update PDN Connection Set Request" } // TEID returns the TEID in uint32. func (m *UpdatePDNConnectionSetRequest) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/update-pdn-connection-set-req_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestUpdatePDNConnectionSetRequest(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewUpdatePDNConnectionSetRequest( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewFullyQualifiedCSID("1.1.1.1", 1), ie.NewFullyQualifiedCSID("1.1.1.1", 1).WithInstance(1), ), Serialized: []byte{ // Header 0x48, 0xc8, 0x00, 0x1e, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // MME-FQ-CSID 0x84, 0x00, 0x07, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, // SGW-FQ-CSID 0x84, 0x00, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseUpdatePDNConnectionSetRequest(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/update-pdn-connection-set-res.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "github.com/wmnsk/go-gtp/gtpv2/ie" ) // UpdatePDNConnectionSetResponse is a UpdatePDNConnectionSetResponse Header and its IEs above. type UpdatePDNConnectionSetResponse struct { *Header Cause *ie.IE PGWFQCSID *ie.IE Recovery *ie.IE PrivateExtension *ie.IE AdditionalIEs []*ie.IE } // NewUpdatePDNConnectionSetResponse creates a new UpdatePDNConnectionSetResponse. func NewUpdatePDNConnectionSetResponse(teid, seq uint32, ies ...*ie.IE) *UpdatePDNConnectionSetResponse { m := &UpdatePDNConnectionSetResponse{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeUpdatePDNConnectionSetResponse, teid, seq, nil, ), } for _, i := range ies { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.FullyQualifiedCSID: m.PGWFQCSID = i case ie.Recovery: m.Recovery = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } m.SetLength() return m } // Marshal returns the byte sequence generated from a UpdatePDNConnectionSetResponse. func (m *UpdatePDNConnectionSetResponse) Marshal() ([]byte, error) { b := make([]byte, m.MarshalLen()) if err := m.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo puts the byte sequence in the byte array given as b. func (m *UpdatePDNConnectionSetResponse) MarshalTo(b []byte) error { if m.Header.Payload != nil { m.Header.Payload = nil } m.Header.Payload = make([]byte, m.MarshalLen()-m.Header.MarshalLen()) offset := 0 if ie := m.Cause; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PGWFQCSID; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.Recovery; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(m.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } m.Header.SetLength() return m.Header.MarshalTo(b) } // ParseUpdatePDNConnectionSetResponse decodes a given byte sequence as a UpdatePDNConnectionSetResponse. func ParseUpdatePDNConnectionSetResponse(b []byte) (*UpdatePDNConnectionSetResponse, error) { m := &UpdatePDNConnectionSetResponse{} if err := m.UnmarshalBinary(b); err != nil { return nil, err } return m, nil } // UnmarshalBinary decodes given byte sequence as UpdatePDNConnectionSetResponse. func (m *UpdatePDNConnectionSetResponse) UnmarshalBinary(b []byte) error { var err error m.Header, err = ParseHeader(b) if err != nil { return err } if len(m.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(m.Header.Payload) if err != nil { return err } for _, i := range decodedIEs { if i == nil { continue } switch i.Type { case ie.Cause: m.Cause = i case ie.FullyQualifiedCSID: m.PGWFQCSID = i case ie.Recovery: m.Recovery = i case ie.PrivateExtension: m.PrivateExtension = i default: m.AdditionalIEs = append(m.AdditionalIEs, i) } } return nil } // MarshalLen returns the serial length of Data. func (m *UpdatePDNConnectionSetResponse) MarshalLen() int { l := m.Header.MarshalLen() - len(m.Header.Payload) if ie := m.Cause; ie != nil { l += ie.MarshalLen() } if ie := m.PGWFQCSID; ie != nil { l += ie.MarshalLen() } if ie := m.Recovery; ie != nil { l += ie.MarshalLen() } if ie := m.PrivateExtension; ie != nil { l += ie.MarshalLen() } for _, ie := range m.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length field. func (m *UpdatePDNConnectionSetResponse) SetLength() { m.Header.Length = uint16(m.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (m *UpdatePDNConnectionSetResponse) MessageTypeName() string { return "Update PDN Connection Set Response" } // TEID returns the TEID in uint32. func (m *UpdatePDNConnectionSetResponse) TEID() uint32 { return m.Header.teid() } ================================================ FILE: gtpv2/message/update-pdn-connection-set-res_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestUpdatePDNConnectionSetResponse(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal", Structured: message.NewUpdatePDNConnectionSetResponse( testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewFullyQualifiedCSID("1.1.1.1", 1), ie.NewRecovery(0xff), ), Serialized: []byte{ // Header 0x48, 0xc9, 0x00, 0x1e, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, // Cause 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, // PGW-FQ-CSID 0x84, 0x00, 0x07, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, // Recovery 0x03, 0x00, 0x01, 0x00, 0xff, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseUpdatePDNConnectionSetResponse(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/message/version-not-supported.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reservev. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import ( "errors" "github.com/wmnsk/go-gtp/gtpv2/ie" ) // VersionNotSupportedIndication is a VersionNotSupportedIndication Header and its IEs above. type VersionNotSupportedIndication struct { *Header AdditionalIEs []*ie.IE } // NewVersionNotSupportedIndication creates a new VersionNotSupportedIndication. func NewVersionNotSupportedIndication(teid, seq uint32, ie ...*ie.IE) *VersionNotSupportedIndication { v := &VersionNotSupportedIndication{ Header: NewHeader( NewHeaderFlags(2, 0, 1), MsgTypeVersionNotSupportedIndication, teid, seq, nil, ), } for _, i := range ie { if i == nil { continue } v.AdditionalIEs = append(v.AdditionalIEs, i) } v.SetLength() return v } // Marshal serializes VersionNotSupportedIndication into bytes. func (v *VersionNotSupportedIndication) Marshal() ([]byte, error) { b := make([]byte, v.MarshalLen()) if err := v.MarshalTo(b); err != nil { return nil, err } return b, nil } // MarshalTo serializes VersionNotSupportedIndication into bytes. func (v *VersionNotSupportedIndication) MarshalTo(b []byte) error { if v.Header.Payload != nil { v.Header.Payload = nil } v.Header.Payload = make([]byte, v.MarshalLen()-v.Header.MarshalLen()) offset := 0 for _, ie := range v.AdditionalIEs { if ie == nil { continue } if err := ie.MarshalTo(v.Header.Payload[offset:]); err != nil { return err } offset += ie.MarshalLen() } v.Header.SetLength() return v.Header.MarshalTo(b) } // ParseVersionNotSupportedIndication decodes given bytes as VersionNotSupportedIndication. func ParseVersionNotSupportedIndication(b []byte) (*VersionNotSupportedIndication, error) { v := &VersionNotSupportedIndication{} if err := v.UnmarshalBinary(b); err != nil { return nil, err } return v, nil } // UnmarshalBinary decodes given bytes as VersionNotSupportedIndication. func (v *VersionNotSupportedIndication) UnmarshalBinary(b []byte) error { var err error v.Header, err = ParseHeader(b) if err != nil { return err } if len(v.Header.Payload) < 2 { return nil } decodedIEs, err := ie.ParseMultiIEs(v.Header.Payload) if err != nil { if errors.Is(err, ErrTooShortToParse) { return nil } return err } for _, i := range decodedIEs { if i == nil { continue } v.AdditionalIEs = append(v.AdditionalIEs, i) } return nil } // MarshalLen returns the serial length in int. func (v *VersionNotSupportedIndication) MarshalLen() int { l := v.Header.MarshalLen() - len(v.Header.Payload) for _, ie := range v.AdditionalIEs { if ie == nil { continue } l += ie.MarshalLen() } return l } // SetLength sets the length in Length fielv. func (v *VersionNotSupportedIndication) SetLength() { v.Header.Length = uint16(v.MarshalLen() - 4) } // MessageTypeName returns the name of protocol. func (v *VersionNotSupportedIndication) MessageTypeName() string { return "Version Not Supported Indication" } // TEID returns the TEID in uint32. func (v *VersionNotSupportedIndication) TEID() uint32 { return v.Header.teid() } ================================================ FILE: gtpv2/message/version-not-supported_deprecated.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message import "log" // Serialize serializes VersionNotSupportedIndication into bytes. // // Deprecated: use VersionNotSupportedIndication.Marshal instead. func (v *VersionNotSupportedIndication) Serialize() ([]byte, error) { log.Println("VersionNotSupportedIndication.Serialize is deprecated. use VersionNotSupportedIndication.Marshal instead") return v.Marshal() } // SerializeTo serializes VersionNotSupportedIndication into bytes given as b. // // Deprecated: use VersionNotSupportedIndication.MarshalTo instead. func (v *VersionNotSupportedIndication) SerializeTo(b []byte) error { log.Println("VersionNotSupportedIndication.SerializeTo is deprecated. use VersionNotSupportedIndication.MarshalTo instead") return v.MarshalTo(b) } // DecodeVersionNotSupportedIndication decodes bytes as VersionNotSupportedIndication. // // Deprecated: use ParseVersionNotSupportedIndication instead. func DecodeVersionNotSupportedIndication(b []byte) (*VersionNotSupportedIndication, error) { log.Println("DecodeVersionNotSupportedIndication is deprecated. use ParseVersionNotSupportedIndication instead") return ParseVersionNotSupportedIndication(b) } // DecodeFromBytes decodes bytes as VersionNotSupportedIndication. // // Deprecated: use VersionNotSupportedIndication.UnmarshalBinary instead. func (v *VersionNotSupportedIndication) DecodeFromBytes(b []byte) error { log.Println("VersionNotSupportedIndication.DecodeFromBytes is deprecated. use VersionNotSupportedIndication.UnmarshalBinary instead") return v.UnmarshalBinary(b) } // Len returns the actual length of VersionNotSupportedIndication. // // Deprecated: use VersionNotSupportedIndication.MarshalLen instead. func (v *VersionNotSupportedIndication) Len() int { log.Println("VersionNotSupportedIndication.Len is deprecated. use VersionNotSupportedIndication.MarshalLen instead") return v.MarshalLen() } ================================================ FILE: gtpv2/message/version-not-supported_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package message_test import ( "testing" "github.com/wmnsk/go-gtp/gtpv2/message" "github.com/wmnsk/go-gtp/gtpv2/testutils" ) func TestVersionNotSupportedIndication(t *testing.T) { cases := []testutils.TestCase{ { Description: "Normal/CauseOnly", Structured: message.NewVersionNotSupportedIndication(testutils.TestBearerInfo.TEID, testutils.TestBearerInfo.Seq), Serialized: []byte{ // Header 0x48, 0x03, 0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x01, 0x00, }, }, } testutils.Run(t, cases, func(b []byte) (testutils.Serializable, error) { v, err := message.ParseVersionNotSupportedIndication(b) if err != nil { return nil, err } v.Payload = nil return v, nil }) } ================================================ FILE: gtpv2/session.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package gtpv2 import ( "net" "sync" "time" "github.com/wmnsk/go-gtp/gtpv2/message" ) // Location is a subscriber's location. type Location struct { MCC, MNC string RATType uint8 LAC, CI, SAI, RAI, TAI uint16 ECI, MeNBI, EMeNBI uint32 } // Subscriber is a subscriber that belongs to a GTPv2 session. type Subscriber struct { IMSI, MSISDN, IMEI string *Location } // Session is a GTPv2 Session. type Session struct { mu sync.Mutex isActive bool *teidMap *bearerMap // channel to store message passed by other Sessions msgQueue chan message.Message // peerAddr is a net.Addr of the peer associated with Session. // To avoid calling String() many times, peerAddrString is set when NewSession // and UpdatePeerAddr is called. peerAddr net.Addr peerAddrString string // Subscriber is a Subscriber associated with Session. *Subscriber } // NewSession creates a new Session with subscriber information. // // This is expected to be used by server-like nodes. Otherwise, use CreateSession(), // which sends Create Session Request and returns a new Session. func NewSession(peerAddr net.Addr, sub *Subscriber) *Session { s := &Session{ mu: sync.Mutex{}, peerAddr: peerAddr, peerAddrString: peerAddr.String(), teidMap: newTeidMap(), bearerMap: newBearerMap("default", &Bearer{QoSProfile: &QoSProfile{}}), Subscriber: sub, msgQueue: make(chan message.Message, 1000), } return s } // Activate marks a Session active. func (s *Session) Activate() error { s.mu.Lock() defer s.mu.Unlock() if s.IMSI == "" { return &RequiredParameterMissingError{"IMSI", "Session must have IMSI set"} } s.isActive = true return nil } // Deactivate marks a Session inactive. func (s *Session) Deactivate() error { s.mu.Lock() defer s.mu.Unlock() s.isActive = false return nil } // IsActive reports whether a Session is active or not. func (s *Session) IsActive() bool { s.mu.Lock() defer s.mu.Unlock() return s.isActive } // PeerAddr returns the address of the peer node associated with Session. func (s *Session) PeerAddr() net.Addr { return s.peerAddr } // UpdatePeerAddr updates the address of the peer node associated with Session. func (s *Session) UpdatePeerAddr(peer net.Addr) { s.peerAddr = peer s.peerAddrString = peer.String() } // AddTEID adds TEID to session with InterfaceType. // // This is used to keep TEIDs of any interface types that may be used later, // including the ones that are assigned to U-Plane. // // For incoming TEID of local interface type, (*Conn).RegisterSession does that // instead of users but it is safe to call it. func (s *Session) AddTEID(ifType uint8, teid uint32) { s.teidMap.store(ifType, teid) } // GetTEID returns TEID associated with InterfaceType given. func (s *Session) GetTEID(ifType uint8) (uint32, error) { if teid, ok := s.teidMap.load(ifType); ok { return teid, nil } return 0, ErrTEIDNotFound } // PassMessageTo passes the message (typically "triggerred message") to the session // expecting to receive it. // // If the message queue of s is full, it waits for certain period of time specified // by timeout. It discards the msg and returns error if expired. // The default queue size of a Session is 1000 and it cannot be configured in the // current implementation. func PassMessageTo(s *Session, msg message.Message, timeout time.Duration) error { select { case s.msgQueue <- msg: return nil case <-time.After(timeout): return ErrTimeout } } // WaitMessage waits for a message to come from other Session. // // It waits for certain period of time specified by timeout, and returns the message // if seq matches the SequenceNumber of message. Otherwise it returns error immediately. func (s *Session) WaitMessage(seq uint32, timeout time.Duration) (message.Message, error) { select { case msg, ok := <-s.msgQueue: if !ok { return nil, &InvalidSessionError{s.IMSI} } if seqGot := msg.Sequence(); seqGot != seq { return nil, &InvalidSequenceError{seqGot} } return msg, nil case <-time.After(timeout): return nil, ErrTimeout } } // AddBearer adds a Bearer to Session with arbitrary name given. // // In the single-bearer environment it is not used, as a bearer named "default" is // always available after created a Session. func (s *Session) AddBearer(name string, br *Bearer) { s.bearerMap.store(name, br) } // RemoveBearer removes a Bearer looked up by name. func (s *Session) RemoveBearer(name string) { s.bearerMap.delete(name) } // RemoveBearerByEBI removes a Bearer looked up by name. func (s *Session) RemoveBearerByEBI(ebi uint8) { name, err := s.LookupBearerNameByEBI(ebi) if err != nil { return } s.bearerMap.delete(name) } // GetDefaultBearer returns the default bearer. func (s *Session) GetDefaultBearer() *Bearer { // it is not expected that the default bearer cannot be found. bearer, ok := s.bearerMap.load("default") if !ok { return nil } return bearer } // SetDefaultBearer sets given bearer as the default bearer. func (s *Session) SetDefaultBearer(bearer *Bearer) { // it is not expected that the default bearer cannot be found. s.bearerMap.store("default", bearer) } // LookupBearerByName looks up Bearer registered in Session by name. func (s *Session) LookupBearerByName(name string) (*Bearer, error) { if br, ok := s.bearerMap.load(name); ok { return br, nil } return nil, &BearerNotFoundError{IMSI: s.IMSI} } // LookupBearerByEBI looks up Bearer registered in Session by EBI. func (s *Session) LookupBearerByEBI(ebi uint8) (*Bearer, error) { var bearer *Bearer s.bearerMap.rangeWithFunc(func(name, br interface{}) bool { b := br.(*Bearer) if ebi == b.EBI { bearer = b return false } return true }) if bearer == nil { return nil, &BearerNotFoundError{IMSI: s.IMSI} } return bearer, nil } // LookupBearerNameByEBI looks up name of Bearer by EBI and returns // its name. func (s *Session) LookupBearerNameByEBI(ebi uint8) (string, error) { var name string s.bearerMap.rangeWithFunc(func(n, br interface{}) bool { bearer := br.(*Bearer) if ebi == bearer.EBI { name = n.(string) return false } return true }) if name == "" { return "", &BearerNotFoundError{IMSI: s.IMSI} } return name, nil } // LookupEBIByName returns EBI associated with name. // // If no EBI found, it returns 0(=invalid value for EBI). func (s *Session) LookupEBIByName(name string) uint8 { if br, ok := s.bearerMap.load(name); ok { return br.EBI } return 0 } // LookupEBIByTEID returns EBI associated with TEID. // // If no EBI found, it returns 0(=invalid value for EBI). func (s *Session) LookupEBIByTEID(teid uint32) uint8 { var ebi uint8 s.bearerMap.rangeWithFunc(func(name, bearer interface{}) bool { br := bearer.(*Bearer) if teid == br.teidIn || teid == br.teidOut { ebi = br.EBI return false } return true }) return ebi } type teidMap struct { syncMap sync.Map } func newTeidMap() *teidMap { return &teidMap{} } func (t *teidMap) store(ifType uint8, teid uint32) { t.syncMap.Store(ifType, teid) } func (t *teidMap) load(ifType uint8) (uint32, bool) { teid, ok := t.syncMap.Load(ifType) if !ok { return 0, false } return teid.(uint32), true } type bearerMap struct { syncMap sync.Map } func newBearerMap(name string, bearer *Bearer) *bearerMap { b := &bearerMap{} b.store(name, bearer) return b } func (b *bearerMap) store(name string, bearer *Bearer) { b.syncMap.Store(name, bearer) } func (b *bearerMap) load(name string) (*Bearer, bool) { bearer, ok := b.syncMap.Load(name) if !ok { return nil, false } return bearer.(*Bearer), true } func (b *bearerMap) delete(name string) { b.syncMap.Delete(name) } func (b *bearerMap) rangeWithFunc(fn func(name, bearer interface{}) bool) { b.syncMap.Range(fn) } // Bearers returns all the bearers registered in Session. func (s *Session) Bearers() []*Bearer { s.mu.Lock() defer s.mu.Unlock() var bs []*Bearer s.bearerMap.rangeWithFunc(func(k, v interface{}) bool { bs = append(bs, v.(*Bearer)) return true }) return bs } // BearerCount returns the number of bearers registered in Session. func (s *Session) BearerCount() int { s.mu.Lock() defer s.mu.Unlock() var count int s.bearerMap.rangeWithFunc(func(k, v interface{}) bool { count++ return true }) return count } ================================================ FILE: gtpv2/testutils/testutils.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Package testutils is an internal package to be used for unit tests. Don't use this. package testutils import ( "testing" "github.com/pascaldekloe/goe/verify" "github.com/wmnsk/go-gtp/gtpv2/message" ) // Serializable is just for testing gtpv2.Messages. Don't use this. type Serializable interface { Marshal() ([]byte, error) MarshalLen() int } // TestCase is just for testing gtpv2.Messages. Don't use this. type TestCase struct { Description string Structured Serializable Serialized []byte } // ParseFunc is just for testing gtpv2.Messages. Don't use this. type ParseFunc func([]byte) (Serializable, error) // TestBearerInfo is just for testing gtpv2.Messages. Don't use this. var TestBearerInfo = struct{ TEID, Seq uint32 }{0x11223344, 0x00000001} // Run is just for testing gtpv2.Messages. Don't use this. func Run(t *testing.T, cases []TestCase, decode ParseFunc) { t.Helper() for _, c := range cases { t.Run(c.Description, func(t *testing.T) { t.Run("Parse", func(t *testing.T) { v, err := decode(c.Serialized) if err != nil { t.Fatal(err) } if got, want := v, c.Structured; !verify.Values(t, "", got, want) { t.Fail() } }) t.Run("Marshal", func(t *testing.T) { b, err := c.Structured.Marshal() if err != nil { t.Fatal(err) } if got, want := b, c.Serialized; !verify.Values(t, "", got, want) { t.Fail() } }) t.Run("Len", func(t *testing.T) { if got, want := c.Structured.MarshalLen(), len(c.Serialized); got != want { t.Fatalf("got %v want %v", got, want) } }) t.Run("Interface", func(t *testing.T) { // Ignore *Header and Generic in this tests. if _, ok := c.Structured.(*message.Header); ok { return } if _, ok := c.Structured.(*message.Generic); ok { return } decoded, err := message.Parse(c.Serialized) if err != nil { t.Fatal(err) } if got, want := decoded.Version(), c.Structured.(message.Message).Version(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := decoded.MessageType(), c.Structured.(message.Message).MessageType(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := decoded.MessageTypeName(), c.Structured.(message.Message).MessageTypeName(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := decoded.TEID(), c.Structured.(message.Message).TEID(); got != want { t.Fatalf("got %v want %v", got, want) } if got, want := decoded.Sequence(), c.Structured.(message.Message).Sequence(); got != want { t.Fatalf("got %v want %v", got, want) } }) }) } } ================================================ FILE: utils/utils.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. // Package utils provides some utilities which might be useful specifically for GTP(or other telco protocols). package utils import ( "encoding/binary" "encoding/hex" "strings" ) // StrToSwappedBytes returns swapped bits from a byte. // It is used for some values where some values are represented in swapped format. // // The second parameter is the hex character(0-f) to fill the last digit when // handling a odd number. "f" is used In most cases. func StrToSwappedBytes(s, filler string) ([]byte, error) { var raw []byte var err error if len(s)%2 == 0 { raw, err = hex.DecodeString(s) } else { raw, err = hex.DecodeString(s + filler) } if err != nil { return nil, err } return swap(raw), nil } // SwappedBytesToStr decodes raw swapped bytes into string. // It is used for some values where some values are represented in swapped format. // // The second parameter is to decide whether to cut the last digit or not. func SwappedBytesToStr(raw []byte, cutLastDigit bool) string { if len(raw) == 0 { return "" } s := hex.EncodeToString(swap(raw)) if cutLastDigit { s = s[:len(s)-1] } return s } func swap(raw []byte) []byte { swapped := make([]byte, len(raw)) for n := range raw { t := ((raw[n] >> 4) & 0xf) + ((raw[n] << 4) & 0xf0) swapped[n] = t } return swapped } // Uint24To32 converts 24bits-length []byte value into the uint32 with 8bits of zeros as prefix. // This function is used for the fields with 3 octets. func Uint24To32(b []byte) uint32 { if len(b) != 3 { return 0 } return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]) } // Uint32To24 converts the uint32 value into 24bits-length []byte. The values in 25-32 bit are cut off. // This function is used for the fields with 3 octets. func Uint32To24(n uint32) []byte { return []byte{uint8(n >> 16), uint8(n >> 8), uint8(n)} } // Uint40To64 converts 40bits-length []byte value into the uint64 with 8bits of zeros as prefix. // This function is used for the fields with 3 octets. func Uint40To64(b []byte) uint64 { if len(b) != 5 { return 0 } return uint64(b[0])<<32 | uint64(b[1])<<24 | uint64(b[2])<<16 | uint64(b[3])<<8 | uint64(b[4]) } // Uint64To40 converts the uint64 value into 40bits-length []byte. The values in 25-64 bit are cut off. // This function is used for the fields with 3 octets. func Uint64To40(n uint64) []byte { return []byte{uint8(n >> 32), uint8(n >> 24), uint8(n >> 16), uint8(n >> 8), uint8(n)} } // EncodePLMN encodes MCC and MNC as BCD-encoded bytes. func EncodePLMN(mcc, mnc string) ([]byte, error) { c, err := StrToSwappedBytes(mcc, "f") if err != nil { return nil, err } n, err := StrToSwappedBytes(mnc, "f") if err != nil { return nil, err } // 2-digit if len(mnc) == 2 { return append(c, n...), nil } // 3-digit b := make([]byte, 3) b[0] = c[0] b[1] = (c[1] & 0x0f) | (n[1] << 4 & 0xf0) b[2] = n[0] return b, nil } // DecodeMCC decodes BCD-encoded MCC as it occurs in CGI/SAI/RAI. func DecodeMNC(b []byte) string { raw := hex.EncodeToString(b) if raw[0] == 'f' { return string([]byte{raw[3], raw[2]}) } return string([]byte{raw[3], raw[2], raw[0]}) } // DecodeMNC decodes BCD-encoded MNC as it occurs in CGI/SAI/RAI func DecodeMCC(b []byte) string { raw := hex.EncodeToString(b) return string([]byte{raw[1], raw[0], raw[3]}) } // DecodePLMN decodes BCD-encoded bytes into MCC and MNC. func DecodePLMN(b []byte) (mcc, mnc string, err error) { mcc = DecodeMCC(b[0:2]) mnc = DecodeMNC(b[1:3]) return } // ParseECI decodes ECI uint32 into e-NodeB ID and Cell ID. func ParseECI(eci uint32) (enbID uint32, cellID uint8, err error) { buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, eci) cellID = buf[3] enbID = binary.BigEndian.Uint32([]byte{0, buf[0], buf[1], buf[2]}) return } // EncodeFQDN encodes the given string as the Name Syntax defined // in RFC 2181, RFC 1035 and RFC 1123. func EncodeFQDN(fqdn string) []byte { b := make([]byte, len(fqdn)+1) var offset = 0 for _, label := range strings.Split(fqdn, ".") { l := len(label) b[offset] = uint8(l) copy(b[offset+1:], label) offset += l + 1 } return b } // DecodeFQDN decodes the given Name Syntax-encoded []byte as a string. func DecodeFQDN(b []byte) string { var ( fqdn []string offset int ) max := len(b) for { if offset >= max { break } l := int(b[offset]) if offset+l+1 > max { break } fqdn = append(fqdn, string(b[offset+1:offset+l+1])) offset += l + 1 } return strings.Join(fqdn, ".") } ================================================ FILE: utils/utils_test.go ================================================ // Copyright 2019-2024 go-gtp authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package utils_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/wmnsk/go-gtp/utils" ) func TestBCDEncoding(t *testing.T) { cases := []struct { description string str string bytes []byte }{ { "imsi", "123451234567890", []byte{0x21, 0x43, 0x15, 0x32, 0x54, 0x76, 0x98, 0xf0}, }, } for _, c := range cases { t.Run("Str2Bytes/"+c.description, func(t *testing.T) { swapped, err := utils.StrToSwappedBytes(c.str, "f") if err != nil { t.Fatal(err) } if diff := cmp.Diff(swapped, c.bytes); diff != "" { t.Error(diff) } }) t.Run("Bytes2Str/"+c.description, func(t *testing.T) { str := utils.SwappedBytesToStr(c.bytes, true) if diff := cmp.Diff(str, c.str); diff != "" { t.Error(diff) } }) } } func TestUint32And24(t *testing.T) { cases := []struct { description string u24 []byte u32 uint32 }{ { "Normal", []byte{0xff, 0xff, 0xff}, 0x00ffffff, }, } for _, c := range cases { t.Run("24To32"+c.description, func(t *testing.T) { converted := utils.Uint24To32(c.u24) if diff := cmp.Diff(converted, c.u32); diff != "" { t.Error(diff) } }) t.Run("32To24"+c.description, func(t *testing.T) { converted := utils.Uint32To24(c.u32) if diff := cmp.Diff(converted, c.u24); diff != "" { t.Error(diff) } }) } } func TestUint64And40(t *testing.T) { cases := []struct { description string u40 []byte u64 uint64 }{ { "Normal", []byte{0xff, 0xff, 0xff, 0xff, 0xff}, 0x000000ffffffffff, }, } for _, c := range cases { t.Run("40To64/"+c.description, func(t *testing.T) { converted := utils.Uint40To64(c.u40) if diff := cmp.Diff(converted, c.u64); diff != "" { t.Error(diff) } }) t.Run("64To40/"+c.description, func(t *testing.T) { converted := utils.Uint64To40(c.u64) if diff := cmp.Diff(converted, c.u40); diff != "" { t.Error(diff) } }) } } func TestPLMN(t *testing.T) { cases := []struct { description string mcc, mnc string encoded []byte }{ { "2-digit", "123", "45", []byte{0x21, 0xf3, 0x54}, }, { "3-digit", "123", "456", []byte{0x21, 0x63, 0x54}, }, } for _, c := range cases { t.Run("serialize/"+c.description, func(t *testing.T) { encoded, err := utils.EncodePLMN(c.mcc, c.mnc) if err != nil { t.Fatal(err) } if diff := cmp.Diff(encoded, c.encoded); diff != "" { t.Error(diff) } }) t.Run("Decode/"+c.description, func(t *testing.T) { mcc, mnc, err := utils.DecodePLMN(c.encoded) if err != nil { t.Fatal(err) } if diff := cmp.Diff(mcc, c.mcc); diff != "" { t.Error(diff) } if diff := cmp.Diff(mnc, c.mnc); diff != "" { t.Error(diff) } }) } }