Compare commits
10 Commits
ce710c3704
...
65de2b35d5
Author | SHA1 | Date |
---|---|---|
Alexander Couzens | 65de2b35d5 | |
Alexander Couzens | fdea0870fa | |
Alexander Couzens | 4dc39ddf46 | |
Alexander Couzens | dce2510f0e | |
Pau Espin | d6600aae20 | |
Pau Espin | 757cca01d8 | |
Pau Espin | bffb425812 | |
Pau Espin | 6b2e105570 | |
Pau Espin | 8c095b84f3 | |
Pau Espin | 9fffb6949f |
8
Makefile
8
Makefile
|
@ -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
|
||||
|
|
|
@ -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
|
||||
%% ===========================================
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ]
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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"}}}
|
||||
]}.
|
||||
|
|
19
rebar.lock
19
rebar.lock
|
@ -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">>}]}
|
||||
].
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
@ -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}).
|
|
@ -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
|
||||
|
|
|
@ -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}.
|
||||
|
||||
|
|
@ -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) ->
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
kernel,
|
||||
stdlib,
|
||||
lager,
|
||||
gtplib,
|
||||
diameter,
|
||||
osmo_gsup,
|
||||
osmo_ss7
|
||||
|
|
|
@ -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]} }.
|
||||
|
|
Loading…
Reference in New Issue