Compare commits

...

10 Commits

Author SHA1 Message Date
Alexander Couzens 65de2b35d5 NOT_FOR_MASTER: configuration for the prototype 2023-12-04 20:47:55 +00:00
Alexander Couzens fdea0870fa WIP: gtp2c/s6b: fix remaining issues... 2023-12-04 20:47:24 +00:00
Alexander Couzens 4dc39ddf46 dia: 3gpp_ts29_273_s6b: add missing include of rfc4004
Erlang compiler doesn't track includes of includes resulting in missing AVPs.
It was missing MIP-Home-Agent-Address.
2023-12-04 20:46:52 +00:00
Alexander Couzens dce2510f0e dia: 3gpp_ts29_273_s6b: add missing *[ AVP ] to AAR
AAR allows to have unknown AVPs.
2023-12-04 20:46:50 +00:00
Pau Espin d6600aae20 s2b: Implement GTPv2C DeleteBearerReq
Sessions are now stored/kept upon CreateSession time until deleted
through DeleteBearerReq.

Related: OS#6046
Change-Id: I1e5af1ead17385d2e494f4c90ffe6455aee850da
2023-10-25 18:03:40 +02:00
Pau Espin 757cca01d8 Fix sysmocom copyright typo
Change-Id: Ia6a3748ac3eb7c5587ed978d60afd315dae07a3c
2023-10-23 14:46:27 +02:00
Pau Espin bffb425812 Initial S6b support
So far only the Rx of AAR msg and Tx of AAA is supported.
This allows already going forward during the session creation, where PGW
sends AAR to the AAA server and expects AAA message.

Related: OS#6229
Change-Id: Ia2b138317cb291a95882853f5403949c5e6a5a1b
2023-10-23 14:44:53 +02:00
Pau Espin 6b2e105570 diameter: Add spec definitions for S6b (TS 28.273 and dep RFC 5777)
S6b interface HSS<->PGW is defined in TS 28.273 section 9.

TS 28.273 depends on some types defined in RFC 5777, which was missing
so far.

Some enumareated types in RFC 5777 whose enums fields are declared in
yet other dependent RFCs are left with a dummy value, as TODO for the
future if they are ever needed.

Related: OS#6229
Change-Id: I8075dd54a5fec386feecdf797d710a2524413acc
2023-10-23 14:44:09 +02:00
Pau Espin 8c095b84f3 Handle GSUP EPDG_Tunnel through GTPv2C CreateSession Req+Resp
Initial GTPv2C infrastructure to send GTPv2C CreateSession Request upon
receival of GSUP EPDG_Tunnel Request, and answer with EPDG_Tunnel
Resp/err when creating the session fails.

Related: OS#6046
Change-Id: I6f00b7fce2d5fcdc484bfd45629b9141f16bc579
2023-10-23 14:42:48 +02:00
Pau Espin 9fffb6949f Makefile: run 'rebar3 clean' upon make clean
Otherwise the diameter files generated during compile in include/ and
src/ are not removed.

Change-Id: I771860f965499bb754c13385819845ef23f33553
2023-10-23 13:11:59 +02:00
18 changed files with 1263 additions and 18 deletions

View File

@ -14,4 +14,12 @@ check:
rebar3 eunit
clean:
# Avoid running rebar3 clean if _build doesn't exist, since it would try
# to fetch deps from the Internet and that may not be avaialble when in
# OBS build.
ifneq ("$(wildcard _build/.*)", "")
rebar3 clean
rm -rf _build/
else
rm -f src/diameter_*.erl
endif

View File

@ -3,17 +3,30 @@
%% ===========================================
{osmo_epdg,
[% GSUP Server connection parameters
{gsup_local_ip, "0.0.0.0"},
{gsup_local_ip, "127.0.0.1"},
{gsup_local_port, 4222},
% Diameter Connection parameters
{diameter_remote_ip, "127.0.0.1"},
% Diameter SWx Connection parameters
{diameter_remote_ip, "10.0.0.21"},
{diameter_remote_port, 3868},
{diameter_proto, sctp},
% Diameter Server parameters
{vendor_id, 0},
{origin_host, "epdg.localdomain"},
{origin_host, "aaa.localdomain"},
{origin_realm, "localdomain"},
{context_id, "epdg@localdomain"}]},
{context_id, "aaa@localdomain"},
% Diameter s6b Connection parameters
{dia_s6b_local_ip, "10.0.0.12"},
{dia_s6b_local_port, 3868},
{dia_s6b_proto, sctp},
{dia_s6b_vendor_id, 0},
{dia_s6b_origin_host, "aaa.localdomain"},
{dia_s6b_origin_realm, "localdomain"},
{dia_s6b_context_id, "aaa@localdomain"},
% GTPv2C Connection parameters
{gtpc_local_ip, "10.0.0.12"},
{gtpc_local_port, 2123},
{gtpc_remote_ip, "10.0.0.22"},
{gtpc_remote_port, 2123}
]},
%% ===========================================
%% SASL config
%% ===========================================

View File

@ -1,7 +1,7 @@
;;
;; %CopyrightBegin%
;;
;; Copyright (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
;; Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
;; Author: Alexander Couzens <lynxis@fe80.eu>
;;
;; This resembles 3GPP TS 29.273 version 15.4.0 Release 15

View File

@ -0,0 +1,168 @@
;;
;; %CopyrightBegin%
;;
;; Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
;; Author: Pau Espin Pedrol <pespin@sysmocom.de>
;;
;; This resembles 3GPP TS 29.273 version 17.6.0 Release 17
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;;
;; %CopyrightEnd%
;;
;;
;; Edits:
;;
;;
@id 16777272
@name diameter_3gpp_ts29_273_s6b
;;@prefix diameter_3gpp
@vendor 10415 3GPP
@inherits diameter_gen_base_rfc6733
@inherits diameter_3gpp_base
@inherits diameter_3gpp_break_circles
@inherits diameter_3gpp_ts29_212
@inherits diameter_3gpp_ts29_214
@inherits diameter_3gpp_ts29_229
@inherits diameter_3gpp_ts29_272
@inherits diameter_3gpp_ts29_273
@inherits diameter_3gpp_ts32_299
@inherits diameter_etsi_es283_034
@inherits diameter_rfc4004
@inherits diameter_rfc4006_cc
@inherits diameter_rfc5447
@inherits diameter_rfc5580
@inherits diameter_rfc5777
@inherits diameter_rfc5778
@inherits diameter_rfc7683
@inherits diameter_rfc7944
@inherits diameter_rfc8583
@messages
;; 9. S6b
;; 9.2.2.2.1 AA-Request (AAR)
AAR ::= < Diameter Header: 265, REQ, PXY >
< Session-Id >
{ Auth-Application-Id }
{ Origin-Host }
{ Origin-Realm }
{ Destination-Realm }
{ Auth-Request-Type }
[ User-Name ]
[ MIP6-Agent-Info ]
[ MIP6-Feature-Vector ]
[ Visited-Network-Identifier ]
[ QoS-Capability ]
[ Service-Selection ]
[ OC-Supported-Features ]
[ Origination-Time-Stamp ]
[ Maximum-Wait-Time ]
*[ Supported-Features ]
[ Emergency-Services ]
*[ AVP ]
;; 9.2.2.2.2 AA-Answer (AAA)
AAA ::= < Diameter Header: 265, PXY >
< Session-Id >
{ Auth-Application-Id }
{ Auth-Request-Type }
{ Result-Code }
{ Origin-Host }
{ Origin-Realm }
[ MIP6-Feature-Vector ]
[ Session-Timeout ]
[ APN-Configuration ]
[ QoS-Resources ]
[ AN-Trusted ]
*[ Redirect-Host ]
[ Trace-Info ]
[ OC-Supported-Features ]
[ OC-OLR ]
*[ Load ]
*[ AVP ]
;; 9.2.2.3.1 Session-Termination-Request (STR)
;; 9.2.2.4.3 Session-Termination-Request (STR)
STR ::= < Diameter Header: 275, REQ, PXY >
< Session-Id >
{ Auth-Application-Id }
{ Origin-Host }
{ Origin-Realm }
{ Destination-Realm }
{ Termination-Cause }
[ User-Name ]
[ OC-Supported-Features ]
*[ AVP ]
;; 9.2.2.3.2 Session-Termination-Answer (STA)
;; 9.2.2.4.4 Session-Termination-Answer (STA)
STA ::= < Diameter Header: 275, PXY >
< Session-Id >
{ Result-Code }
{ Origin-Host }
{ Origin-Realm }
[ OC-Supported-Features ]
[ OC-OLR ]
*[ Load ]
*[ AVP ]
;; 9.2.2.4.1 Abort-Session-Request (ASR)
ASR ::= < Diameter Header: 274, REQ, PXY >
< Session-Id >
{ Origin-Host }
{ Origin-Realm }
{ Destination-Realm }
{ Destination-Host }
{ Auth-Application-Id }
[ User-Name ]
[ Auth-Session-State ]
*[ AVP ]
;; 9.2.2.4.2 Abort-Session-Answer (ASA)
ASA ::= < Diameter Header: 274, PXY >
< Session-Id >
{ Result-Code }
{ Origin-Host }
{ Origin-Realm }
*[ AVP ]
;; TODO: 9.2.2.5 Commands for S6b MIPv4 FACoA Authorization Procedures
;; 9.2.2.6.1 Re-Auth-Request (RAR)
RAR ::= < Diameter Header: 258, REQ, PXY >
< Session-Id >
{ Origin-Host }
{ Origin-Realm }
{ Destination-Realm }
{ Destination-Host }
{ Auth-Application-Id }
{ Re-Auth-Request-Type }
[ User-Name ]
[RAR-Flags ]
*[ AVP ]
;; 9.2.2.6.2 Re-Auth-Answer (RAA)
RAA ::= < Diameter Header: 258, PXY >
< Session-Id >
{ Result-Code }
{ Origin-Host }
{ Origin-Realm }
[ User-Name ]
*[ AVP ]

View File

@ -1,7 +1,7 @@
;;
;; %CopyrightBegin%
;;
;; Copyright (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
;; Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
;; Author: Alexander Couzens <lynxis@fe80.eu>
;;
;; This resembles 3GPP TS 29.273 version 15.4.0 Release 15

View File

@ -1,7 +1,7 @@
;;
;; %CopyrightBegin%
;;
;; Copyright (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
;; Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
;; Author: Alexander Couzens <lynxis@fe80.eu>
;;
;; This resembles 3GPP TS 29.273 version 15.4.0 Release 15

View File

@ -1,7 +1,7 @@
;;
;; %CopyrightBegin%
;;
;; Copyright (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
;; Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
;; Author: Alexander Couzens <lynxis@fe80.eu>
;;
;; This resembles 3GPP TS 29.273 version 15.4.0 Release 15

368
dia/diameter_rfc5777.dia Normal file
View File

@ -0,0 +1,368 @@
;;
;; %CopyrightBegin%
;;
;; Transcribed from RFC 5777 by Pau Espin Pedrol <pespin@sysmocom.de>
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;;
;; %CopyrightEnd%
;;
;;
;; RFC 5777, Traffic Classification and Quality of Service (QoS) Attributes for Diameter
;;
@id 1
@inherits diameter_gen_base_rfc6733
;;@inherits diameter_rfc4004
;; ===========================================================================
@avp_types
QoS-Resources 508 Grouped - ;; 3.1
Filter-Rule 509 Grouped - ;; 3.2
Filter-Rule-Precedence 510 Unsigned32 - ;; 3.3
Classifier 511 Grouped - ;; 4.1.1
Classifier-ID 512 OctetString - ;; 4.1.2
Protocol 513 Enumerated - ;; 4.1.3
Direction 514 Enumerated - ;; 4.1.4
From-Spec 515 Grouped - ;; 4.1.5
To-Spec 516 Grouped - ;; 4.1.6
Negated 517 Enumerated - ;; 4.1.7.1
IP-Address 518 Address - ;; 4.1.7.2
IP-Address-Range 519 Grouped - ;; 4.1.7.3
IP-Address-Start 520 Address - ;; 4.1.7.4
IP-Address-End 521 Address - ;; 4.1.7.5
IP-Address-Mask 522 Grouped - ;; 4.1.7.6
IP-Mask-Bit-Mask-Width 523 Unsigned32 - ;; 4.1.7.7
MAC-Address 524 OctetString - ;; 4.1.7.8
MAC-Address-Mask 525 Grouped - ;; 4.1.7.9
MAC-Address-Mask-Pattern 526 OctetString - ;; 4.1.7.10
EUI64-Address 527 OctetString - ;; 4.1.7.11
EUI64-Address-Mask 528 Grouped - ;; 4.1.7.12
EUI64-Address-Mask-Pattern 529 OctetString - ;; 4.1.7.13
Port 530 Integer32 - ;; 4.1.7.14
Port-Range 531 Grouped - ;; 4.1.7.15
Port-Start 532 Integer32 - ;; 4.1.7.16
Port-End 533 Integer32 - ;; 4.1.7.17
Use-Assigned-Address 534 Enumerated - ;; 4.1.7.18
Diffserv-Code-Point 535 Enumerated - ;; 4.1.8.1
Fragmentation-Flag 536 Enumerated - ;; 4.1.8.2
IP-Option 537 Grouped - ;; 4.1.8.3
IP-Option-Type 538 Enumerated - ;; 4.1.8.4
IP-Option-Value 539 OctetString - ;; 4.1.8.5
TCP-Option 540 Grouped - ;; 4.1.8.6
TCP-Option-Type 541 Enumerated - ;; 4.1.8.7
TCP-Option-Value 542 OctetString - ;; 4.1.8.8
TCP-Flags 543 Grouped - ;; 4.1.8.9
TCP-Flag-Type 544 Unsigned32 - ;; 4.1.8.10
ICMP-Type 545 Grouped - ;; 4.1.8.11
ICMP-Type-Number 546 Enumerated - ;; 4.1.8.12
ICMP-Code 547 Enumerated - ;; 4.1.8.13
ETH-Option 548 Grouped - ;; 4.1.8.14
ETH-Proto-Type 549 Grouped - ;; 4.1.8.15
ETH-Ether-Type 550 OctetString - ;; 4.1.8.16
ETH-SAP 551 OctetString - ;; 4.1.8.17
VLAN-ID-Range 552 Grouped - ;; 4.1.8.18
S-VID-Start 553 Unsigned32 - ;; 4.1.8.19
S-VID-End 554 Unsigned32 - ;; 4.1.8.20
C-VID-Start 555 Unsigned32 - ;; 4.1.8.21
C-VID-End 556 Unsigned32 - ;; 4.1.8.22
User-Priority-Range 557 Grouped - ;; 4.1.8.23
Low-User-Priority 558 Unsigned32 - ;; 4.1.8.24
High-User-Priority 559 Unsigned32 - ;; 4.1.8.25
Time-Of-Day-Condition 560 Grouped - ;; 4.2.1
Time-Of-Day-Start 561 Unsigned32 - ;; 4.2.2
Time-Of-Day-End 562 Unsigned32 - ;; 4.2.3
Day-Of-Week-Mask 563 Unsigned32 - ;; 4.2.4
Day-Of-Month-Mask 564 Unsigned32 - ;; 4.2.5
Month-Of-Year-Mask 565 Unsigned32 - ;; 4.2.6
Absolute-Start-Time 566 Time - ;; 4.2.7
Absolute-Start-Fractional-Seconds 567 Unsigned32 - ;; 4.2.8
Absolute-End-Time 568 Time - ;; 4.2.9
Absolute-End-Fractional-Seconds 569 Unsigned32 - ;; 4.2.10
Timezone-Flag 570 Enumerated - ;; 4.2.11
Timezone-Offset 571 Integer32 - ;; 4.2.12
Treatment-Action 572 Enumerated - ;; 5.1, wrongly defined as Grouped in RFC table
QoS-Profile-Id 573 Unsigned32 - ;; 5.2
QoS-Profile-Template 574 Grouped - ;; 5.3
QoS-Semantics 575 Enumerated - ;; 5.4
QoS-Parameters 576 Grouped - ;; 5.5
Excess-Treatment 577 Grouped - ;; 5.6
QoS-Capability 578 Grouped - ;; 6
;; ===========================================================================
@grouped
;; 3.1
QoS-Resources ::= < AVP Header: 508 >
1*{ Filter-Rule }
*[ AVP ]
;; 3.2
Filter-Rule ::= < AVP Header: 509 >
[ Filter-Rule-Precedence ]
;; Condition part of a Rule
;; ------------------------
[ Classifier ]
*[ Time-Of-Day-Condition ]
;; Action and Meta-Data
;; --------------------
[ Treatment-Action ]
;; Info about QoS related Actions
;; ------------------------------
[ QoS-Semantics ]
[ QoS-Profile-Template ]
[ QoS-Parameters ]
[ Excess-Treatment ]
;; Extension Point
;; ---------------
*[ AVP ]
;; 4.1.1.
Classifier ::= < AVP Header: 511 >
{ Classifier-ID }
[ Protocol ]
[ Direction ]
*[ From-Spec ]
*[ To-Spec ]
*[ Diffserv-Code-Point ]
[ Fragmentation-Flag ]
*[ IP-Option ]
*[ TCP-Option ]
[ TCP-Flags ]
*[ ICMP-Type ]
*[ ETH-Option ]
*[ AVP ]
;; 4.1.5.
From-Spec ::= < AVP Header: 515 >
*[ IP-Address ]
*[ IP-Address-Range ]
*[ IP-Address-Mask ]
*[ MAC-Address ]
*[ MAC-Address-Mask]
*[ EUI64-Address ]
*[ EUI64-Address-Mask]
*[ Port ]
*[ Port-Range ]
[ Negated ]
[ Use-Assigned-Address ]
*[ AVP ]
;; 4.1.6
To-Spec ::= < AVP Header: 516 >
*[ IP-Address ]
*[ IP-Address-Range ]
*[ IP-Address-Mask ]
*[ MAC-Address ]
*[ MAC-Address-Mask]
*[ EUI64-Address ]
*[ EUI64-Address-Mask]
*[ Port ]
*[ Port-Range ]
[ Negated ]
[ Use-Assigned-Address ]
*[ AVP ]
;; 4.1.7.3.
IP-Address-Range ::= < AVP Header: 519 >
[ IP-Address-Start ]
[ IP-Address-End ]
*[ AVP ]
;; 4.1.7.6
IP-Address-Mask ::= < AVP Header: 522 >
{ IP-Address }
{ IP-Mask-Bit-Mask-Width }
*[ AVP ]
;; 4.1.7.9.
MAC-Address-Mask ::= < AVP Header: 525 >
{ MAC-Address }
{ MAC-Address-Mask-Pattern }
*[ AVP ]
;; 4.1.7.12.
EUI64-Address-Mask ::= < AVP Header: 528 >
{ EUI64-Address }
{ EUI64-Address-Mask-Pattern }
*[ AVP ]
;; 4.1.7.15.
Port-Range ::= < AVP Header: 531 >
[ Port-Start ]
[ Port-End ]
*[ AVP ]
;; 4.1.8.3.
IP-Option ::= < AVP Header: 537 >
{ IP-Option-Type }
*[ IP-Option-Value ]
[ Negated ]
*[ AVP ]
;; 4.1.8.6.
TCP-Option ::= < AVP Header: 540 >
{ TCP-Option-Type }
*[ TCP-Option-Value ]
[ Negated ]
*[ AVP ]
;; 4.1.8.9.
TCP-Flags ::= < AVP Header: 543 >
{ TCP-Flag-Type }
[ Negated ]
*[ AVP ]
;; 4.1.8.11
ICMP-Type ::= < AVP Header: 545 >
{ ICMP-Type-Number }
*[ ICMP-Code ]
[ Negated ]
*[ AVP ]
;; 4.1.8.14
ETH-Option ::= < AVP Header: 548 >
{ ETH-Proto-Type }
*[ VLAN-ID-Range ]
*[ User-Priority-Range ]
*[ AVP ]
;; 4.1.8.15
ETH-Proto-Type ::= < AVP Header: 549 >
*[ ETH-Ether-Type ]
*[ ETH-SAP ]
*[ AVP ]
;; 4.1.8.18
VLAN-ID-Range ::= < AVP Header: 552 >
[ S-VID-Start ]
[ S-VID-End ]
[ C-VID-Start ]
[ C-VID-End ]
*[ AVP ]
;; 4.1.8.23
User-Priority-Range ::= < AVP Header: 557 >
*[ Low-User-Priority ]
*[ High-User-Priority ]
*[ AVP ]
;; 4.2.1
Time-Of-Day-Condition ::= < AVP Header: 560 >
[ Time-Of-Day-Start ]
[ Time-Of-Day-End ]
[ Day-Of-Week-Mask ]
[ Day-Of-Month-Mask ]
[ Month-Of-Year-Mask ]
[ Absolute-Start-Time ]
[ Absolute-End-Time ]
[ Timezone-Flag ]
*[ AVP ]
;; 5.3
QoS-Profile-Template ::= < AVP Header: 574 >
{ Vendor-Id }
{ QoS-Profile-Id }
*[ AVP ]
;; 5.5
QoS-Parameters ::= < AVP Header: 576 >
*[ AVP ]
;; 5.6
Excess-Treatment ::= < AVP Header: 577 >
{ Treatment-Action }
[ QoS-Profile-Template ]
[ QoS-Parameters ]
*[ AVP ]
;; 6.
QoS-Capability ::= < AVP Header: 578 >
1*{ QoS-Profile-Template }
*[ AVP ]
;; ===========================================================================
;; 4.1.3.
@enum Protocol
PROTOCOL_TODO_CHECK_RFC2780 0
;; 4.1.4.
@enum Direction
DIRECTION_IN 0
DIRECTION_OUT 1
DIRECTION_BOTH 2
;; 4.1.7.1.
@enum Negated
NEGATED_FALSE 0
NEGATED_TRUE 1
;; 4.1.7.18
@enum Use-Assigned-Address
USE_ASSIGNED_ADDRESS_FALSE 0
USE_ASSIGNED_ADDRESS_TRUE 1
;; 4.1.8.1.
@enum Diffserv-Code-Point
DIFFSERV_CODE_POINT_TODO_CHECK_RFC2474 0
;; 4.1.8.2.
@enum Fragmentation-Flag
FRAGMENTATION_FLAG_DF 0
FRAGMENTATION_FLAG_MF 1
;; 4.1.8.4
@enum IP-Option-Type
IP_OPTION_TYPE_TODO_CHECK_RFC2780 0
;; 4.1.8.7.
@enum TCP-Option-Type
TCP_OPTION_TYPE_TODO_CHECK_RFC2780 0
;; 4.1.8.12.
@enum ICMP-Type-Number
ICMP_TYPE_NUMBER_TODO_CHECK_RFC2780 0
;; 4.1.8.13.
@enum ICMP-Code
ICMP_CODE_TODO_CHECK_RFC2780 0
;; 4.2.11.
@enum Timezone-Flag
TIMEZOME_FLAG_UTC 0
TIMEZOME_FLAG_LOCAL 1
TIMEZOME_FLAG_OFFSET 2
;; 5.1.
@enum Treatment-Action
TREATMENT_ACTION_DROP 0
TREATMENT_ACTION_SHAPE 1
TREATMENT_ACTION_MARK 2
TREATMENT_ACTION_PERMIT 3
;; 5.4.
@enum QoS-Semantics
QOS_SEMANTICS_DESIRED 0
QOS_SEMANTICS_AVAILABLE 1
QOS_SEMANTICS_DELIVERED 2
QOS_SEMANTICS_MINIMUM 3
QOS_SEMANTICS_AUTHORIZE 4

View File

@ -1,7 +1,7 @@
;;
;; %CopyrightBegin%
;;
;; Copyright (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
;; Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
;; Author: Alexander Couzens <lynxis@fe80.eu>
;;
;; This resembles 3GPP TS 29.273 version 15.4.0 Release 15

View File

@ -4,6 +4,7 @@
{deps, [
{lager, {git, "https://github.com/erlang-lager/lager", {tag, "3.9.2"}}},
{gtplib, "3.2.0"},
{osmo_ss7, {git, "https://gitea.osmocom.org/erlang/osmo_ss7", {ref, "9f294d3612f998860004820d1d85b4264721577b"}}},
{osmo_gsup, {git, "https://gitea.osmocom.org/erlang/osmo_gsup", {ref, "07672d8ab1608aa9c9e50ca035521876558fcd42"}}}
]}.

View File

@ -1,16 +1,18 @@
{"1.2.0",
[{<<"epcap">>,
[{<<"cut">>,{pkg,<<"cut">>,<<"1.0.3">>},1},
{<<"epcap">>,
{git,"https://github.com/msantos/epcap",
{ref,"d5c03caf608c1369e68cfed0a606d3eb82ddfd21"}},
1},
{<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.9">>},1},
{<<"gtplib">>,{pkg,<<"gtplib">>,<<"3.2.0">>},0},
{<<"lager">>,
{git,"https://github.com/erlang-lager/lager",
{ref,"459a3b2cdd9eadd29e5a7ce5c43932f5ccd6eb88"}},
0},
{<<"osmo_gsup">>,
{git,"https://gitea.osmocom.org/erlang/osmo_gsup",
{ref,"07672d8ab1608aa9c9e50ca035521876558fcd42"}},
{ref,"e23f118e6e8f7ee1247db34e4cb79e4cecdb0947"}},
0},
{<<"osmo_ss7">>,
{git,"https://gitea.osmocom.org/erlang/osmo_ss7",
@ -23,10 +25,17 @@
{<<"pkt">>,
{git,"https://github.com/msantos/pkt",
{ref,"67a4a14f596fded5ad5f2d8f94318faa8ad2c288"}},
1}]}.
1},
{<<"ppplib">>,{pkg,<<"ppplib">>,<<"1.0.0">>},1}]}.
[
{pkg_hash,[
{<<"goldrush">>, <<"F06E5D5F1277DA5C413E84D5A2924174182FB108DABB39D5EC548B27424CD106">>}]},
{<<"cut">>, <<"1577F2F3BC0F2BF3B97903B7426F8A3D79523687B6A444D0F59A095EF69A0E81">>},
{<<"goldrush">>, <<"F06E5D5F1277DA5C413E84D5A2924174182FB108DABB39D5EC548B27424CD106">>},
{<<"gtplib">>, <<"41E8E14BE21DD6E08B2CBB9D708BCF8FDD47CA49D7FFA480219CAB29F5AE2760">>},
{<<"ppplib">>, <<"F9EC2690532BAF590277A305A2276FCFAD0285557E1055552F8A2FCAF1BF081A">>}]},
{pkg_hash_ext,[
{<<"goldrush">>, <<"99CB4128CFFCB3227581E5D4D803D5413FA643F4EB96523F77D9E6937D994CEB">>}]}
{<<"cut">>, <<"1A4A25DB2B7C5565FD28B314A4EEB898B1ED3CAFFA1AB09149345FB5731ED04B">>},
{<<"goldrush">>, <<"99CB4128CFFCB3227581E5D4D803D5413FA643F4EB96523F77D9E6937D994CEB">>},
{<<"gtplib">>, <<"264626E6993C17B00CA3C083B9BF23D16B88BEEDDDE35F62C954832C21B57AEF">>},
{<<"ppplib">>, <<"32440D630F55DD29F849847DD8F15F69175FDDC210AA88517AC8AD2854CD6FA1">>}]}
].

203
src/aaa_diameter_s6b.erl Normal file
View File

@ -0,0 +1,203 @@
% S6b: AAA side
%
% 3GPP TS 29.273 section 9
%
% (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
% Author: Pau Espin Pedrol <pespin@sysmocom.de>
%
% All Rights Reserved
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU Affero General Public License as
% published by the Free Software Foundation; either version 3 of the
% License, or (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU Affero General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
%
% Additional Permission under GNU AGPL version 3 section 7:
%
% If you modify this Program, or any covered work, by linking or
% combining it with runtime libraries of Erlang/OTP as released by
% Ericsson on http://www.erlang.org (or a modified version of these
% libraries), containing parts covered by the terms of the Erlang Public
% License (http://www.erlang.org/EPLICENSE), the licensors of this
% Program grant you additional permission to convey the resulting work
% without the need to license the runtime libraries of Erlang/OTP under
% the GNU Affero General Public License. Corresponding Source for a
% non-source form of such a combination shall include the source code
% for the parts of the runtime libraries of Erlang/OTP used as well as
% that of the covered work.
-module(aaa_diameter_s6b).
-author('Pau Espin Pedrol <pespin@sysmocom.de>').
-behaviour(gen_server).
-include_lib("diameter_3gpp_ts29_273_s6b.hrl").
-include_lib("diameter/include/diameter_gen_base_rfc6733.hrl").
%% API Function Exports
-export([start_link/0]).
-export([start/0, stop/0, terminate/2]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-export([code_change/3]).
-export([multimedia_auth_request/6]).
-export([server_assignment_request/3]).
-export([test/0, test/1]).
%% Diameter Application Definitions
-define(SERVER, ?MODULE).
-define(SVC_NAME, ?MODULE).
-define(APP_ALIAS, ?MODULE).
-define(CALLBACK_MOD, aaa_diameter_s6b_cb).
-define(DIAMETER_DICT_S6b, diameter_3gpp_ts29_273_s6b).
-define(ENV_APP_NAME, osmo_epdg).
-define(ENV_DEFAULT_SESSION_ID, "aaa@localdomain").
-define(ENV_DEFAULT_ORIG_REALM, "localdomain").
-define(ENV_DEFAULT_ORIG_HOST, "aaa.localdomain").
-define(ENV_DEFAULT_VENDOR_ID, 0).
-define(ENV_DEFAULT_DIAMETER_PROTO, sctp).
-define(ENV_DEFAULT_DIAMETER_REMOTE_IP, "127.0.0.10").
-define(ENV_DEFAULT_DIAMETER_REMOTE_PORT, 3868).
-define(VENDOR_ID_3GPP, 10415).
-define(VENDOR_ID_3GPP2, 5535).
-define(VENDOR_ID_ETSI, 13019).
-define(DIAMETER_APP_ID_S6b, ?DIAMETER_DICT_S6b:id()).
%% The service configuration. As in the server example, a client
%% supporting multiple Diameter applications may or may not want to
%% configure a common callback module on all applications.
-define(SERVICE,
[{'Origin-Host', application:get_env(?ENV_APP_NAME, dia_s6b_origin_host, ?ENV_DEFAULT_ORIG_HOST)},
{'Origin-Realm', application:get_env(?ENV_APP_NAME, dia_s6b_origin_realm, ?ENV_DEFAULT_ORIG_REALM)},
{'Vendor-Id', application:get_env(?ENV_APP_NAME, dia_s6b_vendor_id, ?ENV_DEFAULT_VENDOR_ID)},
{'Vendor-Specific-Application-Id',
[#'diameter_base_Vendor-Specific-Application-Id'{
'Vendor-Id' = ?VENDOR_ID_3GPP,
'Auth-Application-Id' = [?DIAMETER_APP_ID_S6b]}]},
{'Product-Name', "osmo-epdg-AAA"},
% TODO: check which we should annouce here as Supported-Vendor-Id
{'Supported-Vendor-Id', [?VENDOR_ID_3GPP, ?VENDOR_ID_ETSI, ?VENDOR_ID_3GPP2]},
{ application,
[{alias, ?APP_ALIAS},
{dictionary, ?DIAMETER_DICT_S6b},
{module, ?CALLBACK_MOD},
{answer_errors, callback}]}]).
-record(state, {
handlers,
peers = #{}
}).
%% @doc starts gen_server implementation process
-spec start() -> ok | {error, term()}.
start() ->
application:ensure_all_started(?MODULE),
start_link().
%% @doc stops gen_server implementation process
-spec stop() -> ok.
stop() ->
gen_server:cast(?SERVER, stop).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
peer_down(API, SvcName, {PeerRef, _} = Peer) ->
% fixme: why do we still have ets here?
(catch ets:delete(?MODULE, {API, PeerRef})),
gen_server:cast(?SERVER, {peer_down, SvcName, Peer}),
ok.
init(State) ->
Proto = application:get_env(?ENV_APP_NAME, dia_s6b_proto, ?ENV_DEFAULT_DIAMETER_PROTO),
Ip = application:get_env(?ENV_APP_NAME, dia_s6b_local_ip, ?ENV_DEFAULT_DIAMETER_REMOTE_IP),
Port = application:get_env(?ENV_APP_NAME, dia_s6b_local_port, ?ENV_DEFAULT_DIAMETER_REMOTE_PORT),
ok = diameter:start_service(?MODULE, ?SERVICE),
% lager:info("DiaServices is ~p~n", [DiaServ]),
{ok, _} = listen({address, Proto, Ip, Port}),
{ok, State}.
test() ->
test("001011234567890").
test(IMSI) ->
multimedia_auth_request(IMSI, 3, "EAP-AKA", 1, [], []).
multimedia_auth_request(IMSI, NumAuthItems, AuthScheme, RAT, CKey, IntegrityKey) ->
gen_server:call(?SERVER,
{mar, {IMSI, NumAuthItems, AuthScheme, RAT, CKey, IntegrityKey}}).
% APN is optional and should be []
server_assignment_request(IMSI, Type, APN) ->
gen_server:call(?SERVER,
{sar, {IMSI, Type, APN}}).
result_code_success(2001) -> ok;
result_code_success(2002) -> ok;
result_code_success(_) -> invalid_result_code.
handle_call({aar, {IMSI, Type, APN}}, _From, State) ->
SessionId = diameter:session_id(application:get_env(?ENV_APP_NAME, origin_host, ?ENV_DEFAULT_ORIG_HOST)).
%% @callback gen_server
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(_Req, State) ->
{noreply, State}.
%% @callback gen_server
handle_info(_Info, State) ->
{noreply, State}.
%% @callback gen_server
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% @callback gen_server
terminate(normal, _State) ->
diameter:stop_service(?SVC_NAME),
ok;
terminate(shutdown, _State) ->
ok;
terminate({shutdown, _Reason}, _State) ->
ok;
terminate(_Reason, _State) ->
ok.
%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
%% connect/2
listen(Name, {address, Protocol, IPAddr, Port}) ->
lager:notice("~s Listening on IP ~s port ~p~n", [Name, IPAddr, Port]),
{ok, IP} = inet_parse:address(IPAddr),
TransportOpts =
[{transport_module, tmod(Protocol)},
{transport_config,
[{reuseaddr, true},
{ip, IP},
{port, Port}
%%{raddr, IP},
%%{rport, Port}
]}],
diameter:add_transport(Name, {listen, [{reconnect_timer, 1000} | TransportOpts]}).
listen(Address) ->
listen(?SVC_NAME, Address).
%% Convert connection type
tmod(tcp) ->
diameter_tcp;
tmod(sctp) ->
diameter_sctp.

View File

@ -0,0 +1,73 @@
%%
%% The diameter application callback module configured by client.erl.
%%
-module(aaa_diameter_s6b_cb).
-include_lib("diameter/include/diameter.hrl").
-include_lib("diameter_3gpp_ts29_273_s6b.hrl").
%% diameter callbacks
-export([peer_up/3, peer_down/3, pick_peer/4, prepare_request/3, prepare_retransmit/3,
handle_answer/4, handle_error/4, handle_request/3]).
-define(UNEXPECTED, erlang:error({unexpected, ?MODULE, ?LINE})).
%% peer_up/3
peer_up(_SvcName, Peer, State) ->
lager:info("Peer up: ~p~n", [Peer]),
State.
%% peer_down/3
peer_down(_SvcName, Peer, State) ->
lager:info("Peer down: ~p~n", [Peer]),
State.
%% pick_peer/4
pick_peer([_Peer | _], _, _SvcName, _State) ->
?UNEXPECTED.
%% prepare_request/3
prepare_request(_, _SvcName, _Peer) ->
?UNEXPECTED.
%% prepare_retransmit/3
prepare_retransmit(_Packet, _SvcName, _Peer) ->
?UNEXPECTED.
%% handle_answer/4
%% Since client.erl has detached the call when using the list
%% encoding and not otherwise, output to the terminal in the
%% the former case, return in the latter.
handle_answer(_Packet, _Request, _SvcName, _Peer) ->
?UNEXPECTED.
%% handle_error/4
handle_error(Reason, Request, _SvcName, _Peer) when is_list(Request) ->
lager:error("Request error: ~p~n", [Reason]),
?UNEXPECTED.
handle_request(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps}) when is_record(Req, 'AAR') ->
lager:info("S6b Rx from ~p: ~p~n", [Caps, Req]),
% extract relevant fields from DIAMETER AAR
#diameter_caps{origin_host = {OH,_}, origin_realm = {OR,_}} = Caps,
#'AAR'{'Session-Id' = SessionId,
'Auth-Application-Id' = AuthAppId,
'Auth-Request-Type' = AuthReqType,
'User-Name' = _UserName} = Req,
Resp = #'AAA'{'Session-Id'=SessionId,
'Auth-Application-Id' = AuthAppId,
'Auth-Request-Type' = AuthReqType,
'Result-Code'=2001,
'Origin-Host'=OH,
'Origin-Realm'=OR},
lager:info("S6b Tx to ~p: ~p~n", [Caps, Resp]),
{reply, Resp};
% TODO: extract relevant fields from DIAMETER AAA
%% handle_request/3
handle_request(Packet, _SvcName, Peer) ->
lager:error("S6b Rx unexpected msg from ~p: ~p~n", [Peer, Packet]),
%PESPIN: TODO: handle S6b AAR here, see osmo_dia2gsup "handle_request" as example.
erlang:error({unexpected, ?MODULE, ?LINE}).

View File

@ -2,7 +2,7 @@
%
% TS 29.273
%
% (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
% (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
% Author: Alexander Couzens <lynxis@fe80.eu>
%
% All Rights Reserved

366
src/epdg_gtpc_s2b.erl Normal file
View File

@ -0,0 +1,366 @@
% S2b: GTPv2C towards PGW
%
% 3GPP TS 29.274
%
% (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
% Author: Pau Espin Pedrol <pespin@sysmocom.de>
%
% All Rights Reserved
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU Affero General Public License as
% published by the Free Software Foundation; either version 3 of the
% License, or (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU Affero General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
%
% Additional Permission under GNU AGPL version 3 section 7:
%
% If you modify this Program, or any covered work, by linking or
% combining it with runtime libraries of Erlang/OTP as released by
% Ericsson on http://www.erlang.org (or a modified version of these
% libraries), containing parts covered by the terms of the Erlang Public
% License (http://www.erlang.org/EPLICENSE), the licensors of this
% Program grant you additional permission to convey the resulting work
% without the need to license the runtime libraries of Erlang/OTP under
% the GNU Affero General Public License. Corresponding Source for a
% non-source form of such a combination shall include the source code
% for the parts of the runtime libraries of Erlang/OTP used as well as
% that of the covered work.
-module(epdg_gtpc_s2b).
-author('Pau Espin Pedrol <pespin@sysmocom.de>').
-behaviour(gen_server).
-include_lib("gtplib/include/gtp_packet.hrl").
%% API Function Exports
-export([start_link/5]).
-export([terminate/2]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-export([code_change/3]).
-export([create_session_req/1]).
%% Application Definitions
-define(SERVER, ?MODULE).
-define(SVC_NAME, ?MODULE).
-define(APP_ALIAS, ?MODULE).
-define(CALLBACK_MOD, epdg_gtpc_s2b_cb).
-define(ENV_APP_NAME, osmo_epdg).
%% TODO: make APN configurable? get it from HSS?
-define(APN, <<"internet">>).
-define(MCC, 901).
-define(MNC, 42).
-record(gtp_state, {
socket,
laddr_str,
laddr :: inet:ip_address(),
lport :: non_neg_integer(),
raddr_str,
raddr :: inet:ip_address(),
rport :: non_neg_integer(),
restart_counter :: 0..255,
seq_no :: 0..16#ffffffff,
next_local_control_tei = 1 :: 0..16#ffffffff,
next_local_data_tei = 1 :: 0..16#ffffffff,
sessions = sets:new()
}).
-record(gtp_bearer, {
ebi :: non_neg_integer(),
local_data_tei = 0 :: non_neg_integer(),
remote_data_tei = 0 :: non_neg_integer()
}).
-record(gtp_session, {
imsi :: binary(),
apn :: binary(),
ue_ip :: inet:ip_address(),
local_control_tei = 0 :: non_neg_integer(),
remote_control_tei = 0 :: non_neg_integer(),
bearer :: gtp_bearer %% FIXME: only one bearer for now
}).
start_link(LocalAddr, LocalPort, RemoteAddr, RemotePort, Options) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [LocalAddr, LocalPort, RemoteAddr, RemotePort, Options], []).
peer_down(API, SvcName, {PeerRef, _} = Peer) ->
% fixme: why do we still have ets here?
(catch ets:delete(?MODULE, {API, PeerRef})),
gen_server:cast(?SERVER, {peer_down, SvcName, Peer}),
ok.
% from travelping
ip_to_bin({A, B, C, D}) ->
<<A, B, C, D>>.
plmn(CC, NC, NCSize) ->
MCC = iolist_to_binary(io_lib:format("~3..0b", [CC])),
MNC = iolist_to_binary(io_lib:format("~*..0b", [NCSize, NC])),
{MCC, MNC}.
init(State) ->
lager:info("epdg_gtpc_s2b: init(): ~p", [State]),
[LocalAddr | [LocalPort | [RemoteAddr | [RemotePort | _]]]] = State,
lager:info("epdg_gtpc_s2b: Binding to IP ~s port ~p~n", [LocalAddr, LocalPort]),
{ok, LocalAddrInet} = inet_parse:address(LocalAddr),
{ok, RemoteAddrInet} = inet_parse:address(RemoteAddr),
Opts = [
binary,
{ip, LocalAddrInet},
{active, true},
{reuseaddr, true}
],
Ret = gen_udp:open(LocalPort, Opts),
case Ret of
{ok, Socket} ->
lager:info("epdg_gtpc_s2b: Socket is ~p~n", [Socket]),
ok = connect({Socket, RemoteAddr, RemotePort}),
St = #gtp_state{
socket = Socket,
laddr_str = LocalAddr,
laddr = LocalAddrInet,
lport = LocalPort,
raddr_str = RemoteAddr,
raddr = RemoteAddrInet,
rport = RemotePort,
restart_counter = 0,
seq_no = 0
},
{ok, St};
{error, Reason} ->
lager:error("GTPv2C UDP socket open error: ~w~n", [Reason])
end.
create_session_req(Imsi) ->
gen_server:call(?SERVER,
{gtpc_create_session_req, {Imsi}}).
handle_call({gtpc_create_session_req, {Imsi}}, _From, State0) ->
{Sess0, State1} = find_or_new_gtp_session(Imsi, State0),
Req = gen_create_session_request(Sess0, State1),
%TODO: increment State.seq_no.
tx_gtp(Req, State1),
lager:debug("Waiting for CreateSessionResponse~n", []),
receive
{udp, _Socket, IP, InPortNo, RxMsg} ->
try
Resp = gtp_packet:decode(RxMsg),
lager:info("s2b: Rx from IP ~p port ~p ~p~n", [IP, InPortNo, Resp]),
Sess1 = update_gtp_session_from_create_session_response(Resp, Sess0),
lager:info("s2b: Updated Session after create_session_response: ~p~n", [Sess1]),
State2 = update_gtp_session(Sess0, Sess1, State1),
{reply, {ok, Resp}, State2}
catch Any ->
lager:error("Error sending message to receiver, ERROR: ~p~n", [Any]),
{reply, {error, decode_failure}, State1}
end
after 5000 ->
lager:error("Timeout waiting for CreateSessionResponse for ~p~n", [Req]),
{reply, timeout, State1}
end.
%% @callback gen_server
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(Req, State) ->
lager:info("S2b handle_cast: ~p ~n", [Req]),
{noreply, State}.
%% @callback gen_server
handle_info({udp, _Socket, IP, InPortNo, RxMsg}, State) ->
lager:info("S2b: Rx from IP ~p port ~p: ~p~n", [IP, InPortNo, RxMsg]),
Req = gtp_packet:decode(RxMsg),
lager:info("S2b: Rx from IP ~p port ~p: ~p~n", [IP, InPortNo, Req]),
rx_gtp(Req, State);
handle_info(Info, State) ->
lager:info("S2b handle_info: ~p ~n", [Info]),
{noreply, State}.
%% @callback gen_server
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% @callback gen_server
terminate(normal, State) ->
udp_gen:close(State#gtp_state.socket),
ok;
terminate(shutdown, _State) ->
ok;
terminate({shutdown, _Reason}, _State) ->
ok;
terminate(_Reason, _State) ->
ok.
%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
new_gtp_session(Imsi, State) ->
% TODO: find non-used local TEI inside State
Bearer = #gtp_bearer{
ebi = 5,
local_data_tei = State#gtp_state.next_local_data_tei
},
Sess = #gtp_session{imsi = Imsi,
apn = ?APN,
local_control_tei = State#gtp_state.next_local_control_tei,
bearer = Bearer
},
NewSt = State#gtp_state{next_local_control_tei = State#gtp_state.next_local_control_tei + 1,
next_local_data_tei = State#gtp_state.next_local_data_tei + 1,
sessions = sets:add_element(Sess, State#gtp_state.sessions)},
{Sess, NewSt}.
% returns Sess if found, undefined it not
find_gtp_session_by_imsi(Imsi, State) ->
sets:fold(
fun(SessIt = #gtp_session{imsi = Imsi}, _AccIn) -> SessIt;
(_, AccIn) -> AccIn
end,
undefined,
State#gtp_state.sessions).
find_or_new_gtp_session(Imsi, State) ->
Sess = find_gtp_session_by_imsi(Imsi, State),
case Sess of
#gtp_session{imsi = Imsi} ->
{Sess, State};
undefined ->
new_gtp_session(Imsi, State)
end.
update_gtp_session(OldSess, NewSess, State) ->
SetRemoved = sets:del_element(OldSess, State#gtp_state.sessions),
SetUpdated = sets:add_element(NewSess, SetRemoved),
State#gtp_state{sessions = SetUpdated}.
delete_gtp_session(Sess, State) ->
SetRemoved = sets:del_element(Sess, State#gtp_state.sessions),
State#gtp_state{sessions = SetRemoved}.
update_gtp_session_from_create_session_response_ie(none, Sess) ->
Sess;
update_gtp_session_from_create_session_response_ie({_,
#v2_fully_qualified_tunnel_endpoint_identifier{
interface_type = _Interface,
key = TEI, ipv4 = _IP4, ipv6 = _IP6},
Next}, Sess) ->
update_gtp_session_from_create_session_response_ie(maps:next(Next), Sess#gtp_session{remote_control_tei = TEI});
update_gtp_session_from_create_session_response_ie({_, _, Next},
Sess) ->
update_gtp_session_from_create_session_response_ie(maps:next(Next), Sess).
update_gtp_session_from_create_session_response_ies(#gtp{ie = IEs}, Sess) ->
update_gtp_session_from_create_session_response_ie(maps:next(maps:iterator(IEs)), Sess).
update_gtp_session_from_create_session_response(Resp = #gtp{version = v2, type = create_session_response}, Sess) ->
update_gtp_session_from_create_session_response_ies(#gtp{ie = Resp#gtp.ie}, Sess).
% returns Sess if found, undefined it not
find_gtp_session_by_local_teic(LocalControlTei, State) ->
sets:fold(
fun(SessIt = #gtp_session{local_control_tei = LocalControlTei}, _AccIn) -> SessIt;
(_, AccIn) -> AccIn
end,
undefined,
State#gtp_state.sessions).
%% connect/2
connect(Name, {Socket, RemoteAddr, RemotePort}) ->
lager:info("~s connecting to IP ~s port ~p~n", [Name, RemoteAddr, RemotePort]),
gen_udp:connect(Socket, RemoteAddr, RemotePort).
connect(Address) ->
connect(?SVC_NAME, Address).
rx_gtp(Req = #gtp{version = v2, type = delete_bearer_request}, State) ->
Sess = find_gtp_session_by_local_teic(Req#gtp.tei, State),
case Sess of
undefined ->
lager:error("Rx unknown TEI ~p: ~p~n", [Req#gtp.tei, Req]),
{noreply, State};
Sess ->
Resp = gen_delete_bearer_response(Req, Sess, request_accepted, State),
tx_gtp(Resp, State),
State1 = delete_gtp_session(Sess, State),
{noreply, State1}
end;
rx_gtp(Req, State) ->
lager:error("S2b: UNIMPLEMENTED Rx: ~p~n", [Req]),
{noreply, State}.
tx_gtp(Req, State) ->
lager:info("s2b: Tx ~p~n", [Req]),
Msg = gtp_packet:encode(Req),
gen_udp:send(State#gtp_state.socket, State#gtp_state.raddr, State#gtp_state.rport, Msg).
%% 7.2.1 Create Session Request
gen_create_session_request(#gtp_session{imsi = Imsi,
apn = Apn,
local_control_tei = LocalCtlTEI,
bearer = Bearer},
#gtp_state{laddr = LocalAddr,
restart_counter = RCnt,
seq_no = SeqNo}) ->
BearersIE = [#v2_bearer_level_quality_of_service{
pci = 1, pl = 10, pvi = 0, label = 8,
maximum_bit_rate_for_uplink = 0,
maximum_bit_rate_for_downlink = 0,
guaranteed_bit_rate_for_uplink = 0,
guaranteed_bit_rate_for_downlink = 0
},
#v2_eps_bearer_id{eps_bearer_id = Bearer#gtp_bearer.ebi},
#v2_fully_qualified_tunnel_endpoint_identifier{
instance = Bearer#gtp_bearer.ebi,
interface_type = 31, %% "S2b-U ePDG GTP-U"
key = Bearer#gtp_bearer.local_data_tei,
ipv4 = ip_to_bin(LocalAddr)
}
],
IEs = [#v2_recovery{restart_counter = RCnt},
#v2_international_mobile_subscriber_identity{imsi = Imsi},
#v2_rat_type{rat_type = 3}, %% 3 = WLAN
#v2_fully_qualified_tunnel_endpoint_identifier{
instance = 0,
interface_type = 30, %% "S2b ePDG GTP-C"
key = LocalCtlTEI,
ipv4 = ip_to_bin(LocalAddr)
},
#v2_access_point_name{instance = 0, apn = [Apn]},
#v2_selection_mode{mode = 0},
#v2_pdn_address_allocation{type = ipv4, address = <<0,0,0,0>>},
#v2_bearer_context{group = BearersIE},
#v2_serving_network{
instance = 0,
plmn_id = plmn(?MCC, ?MNC, 3)
}
],
#gtp{version = v2, type = create_session_request, tei = 0, seq_no = SeqNo, ie = IEs}.
gen_delete_bearer_response(Req = #gtp{version = v2, type = delete_bearer_request},
Sess = #gtp_session{remote_control_tei = RemoteCtlTEI},
GtpCause,
#gtp_state{restart_counter = RCnt}) ->
IEs = [#v2_recovery{restart_counter = RCnt},
#v2_cause{v2_cause = GtpCause}
],
#gtp{version = v2,
type = delete_bearer_response,
tei = RemoteCtlTEI,
seq_no = Req#gtp.seq_no,
ie = IEs}.

View File

@ -177,6 +177,23 @@ handle_info({ipa, Socket, ?IPAC_PROTO_EXT_GSUP, GsupMsgRx = #{message_type := lo
% epdg tunnel request / trigger the establishment to the PGW and prepares everything for the user traffic to flow
% When sending a epdg_tunnel_response everything must be ready for the UE traffic
handle_info({ipa, Socket, ?IPAC_PROTO_EXT_GSUP, GsupMsgRx = #{message_type := epdg_tunnel_request, imsi := Imsi}}, S) ->
lager:info("GSUP: Rx ~p~n", [GsupMsgRx]),
Result = epdg_gtpc_s2b:create_session_req(Imsi),
case Result of
{ok, _} ->
Resp = #{message_type => epdg_tunnel_result,
imsi => Imsi,
message_class => 5
};
{error, _} ->
Resp = #{message_type => epdg_tunnel_error,
imsi => Imsi,
message_class => 5,
cause => 16#11 % FIXME: Use proper defines as cause code and use Network failure
}
end,
lager:info("GSUP: Tx ~p~n", [Resp]),
ipa_proto:send(Socket, ?IPAC_PROTO_EXT_GSUP, Resp),
{noreply, S};
handle_info(Info, S) ->

View File

@ -8,6 +8,7 @@
kernel,
stdlib,
lager,
gtplib,
diameter,
osmo_gsup,
osmo_ss7

View File

@ -8,6 +8,10 @@
-define(ENV_APP_NAME, osmo_epdg).
-define(ENV_DEFAULT_GSUP_LOCAL_IP, "0.0.0.0").
-define(ENV_DEFAULT_GSUP_LOCAL_PORT, 4222).
-define(ENV_DEFAULT_GTPC_LOCAL_IP, "127.0.0.2").
-define(ENV_DEFAULT_GTPC_LOCAL_PORT, 2123).
-define(ENV_DEFAULT_GTPC_REMOTE_IP, "127.0.0.1").
-define(ENV_DEFAULT_GTPC_REMOTE_PORT, 2123).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
@ -15,11 +19,25 @@ start_link() ->
init([]) ->
GsupLocalIp = application:get_env(?ENV_APP_NAME, gsup_local_ip, ?ENV_DEFAULT_GSUP_LOCAL_IP),
GsupLocalPort = application:get_env(?ENV_APP_NAME, gsup_local_port, ?ENV_DEFAULT_GSUP_LOCAL_PORT),
GtpcLocalIp = application:get_env(?ENV_APP_NAME, gtpc_local_ip, ?ENV_DEFAULT_GTPC_LOCAL_IP),
GtpcLocalPort = application:get_env(?ENV_APP_NAME, gtpc_local_port, ?ENV_DEFAULT_GTPC_LOCAL_PORT),
GtpcRemoteIp = application:get_env(?ENV_APP_NAME, gtpc_remote_ip, ?ENV_DEFAULT_GTPC_REMOTE_IP),
GtpcRemotePort = application:get_env(?ENV_APP_NAME, gtpc_remote_port, ?ENV_DEFAULT_GTPC_REMOTE_PORT),
DiaServer = {epdg_diameter_swx, {epdg_diameter_swx,start_link,[]},
permanent,
5000,
worker,
[epdg_diameter_swx_cb]},
DiaS6bServer = {aaa_diameter_s6b, {aaa_diameter_s6b,start_link,[]},
permanent,
5000,
worker,
[aaa_diameter_s6b_cb]},
GtpcServer = {epdg_gtpc_s2b, {epdg_gtpc_s2b,start_link, [GtpcLocalIp, GtpcLocalPort, GtpcRemoteIp, GtpcRemotePort, []]},
permanent,
5000,
worker,
[epdg_gtpc_s2b]},
GsupServer = {gsup_server, {gsup_server, start_link, [GsupLocalIp, GsupLocalPort, []]},
permanent,
5000,
@ -30,4 +48,4 @@ init([]) ->
5000,
worker,
[auth_handler]},
{ok, { {one_for_all, 5, 10}, [DiaServer, GsupServer, AuthHandler]} }.
{ok, { {one_for_all, 5, 10}, [DiaServer, DiaS6bServer, GtpcServer, GsupServer, AuthHandler]} }.