aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src/base/diameter_session.erl
blob: c5ea0428b5a28283b13b8e054ba99eec16efe663 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%

-module(diameter_session).

-export([sequence/0,
         sequence/1,
         session_id/1,
         origin_state_id/0]).

%% towards diameter_sup
-export([init/0]).

-define(INT64, 16#FFFFFFFFFFFFFFFF).
-define(INT32, 16#FFFFFFFF).

%% ---------------------------------------------------------------------------
%% # sequence/0-1
%%
%% Output: 32-bit
%% ---------------------------------------------------------------------------

%% 3588, 3:
%%
%%    Hop-by-Hop Identifier
%%       The Hop-by-Hop Identifier is an unsigned 32-bit integer field (in
%%       network byte order) and aids in matching requests and replies.
%%       The sender MUST ensure that the Hop-by-Hop identifier in a request
%%       is unique on a given connection at any given time, and MAY attempt
%%       to ensure that the number is unique across reboots.  The sender of
%%       an Answer message MUST ensure that the Hop-by-Hop Identifier field
%%       contains the same value that was found in the corresponding
%%       request.  The Hop-by-Hop identifier is normally a monotonically
%%       increasing number, whose start value was randomly generated.  An
%%       answer message that is received with an unknown Hop-by-Hop
%%       Identifier MUST be discarded.
%%
%%    End-to-End Identifier
%%       The End-to-End Identifier is an unsigned 32-bit integer field (in
%%       network byte order) and is used to detect duplicate messages.
%%       Upon reboot implementations MAY set the high order 12 bits to
%%       contain the low order 12 bits of current time, and the low order
%%       20 bits to a random value.  Senders of request messages MUST
%%       insert a unique identifier on each message.  The identifier MUST
%%       remain locally unique for a period of at least 4 minutes, even
%%       across reboots.  The originator of an Answer message MUST ensure
%%       that the End-to-End Identifier field contains the same value that
%%       was found in the corresponding request.  The End-to-End Identifier
%%       MUST NOT be modified by Diameter agents of any kind.  The
%%       combination of the Origin-Host (see Section 6.3) and this field is
%%       used to detect duplicates.  Duplicate requests SHOULD cause the
%%       same answer to be transmitted (modulo the hop-by-hop Identifier
%%       field and any routing AVPs that may be present), and MUST NOT
%%       affect any state that was set when the original request was
%%       processed.  Duplicate answer messages that are to be locally
%%       consumed (see Section 6.2) SHOULD be silently discarded.

-spec sequence()
   -> diameter:'Unsigned32'().

sequence() ->
    Instr = {_Pos = 2, _Incr = 1, _Threshold = ?INT32, _SetVal = 0},
    ets:update_counter(diameter_sequence, sequence, Instr).

-spec sequence(diameter:sequence())
   -> diameter:'Unsigned32'().

sequence({_,32}) ->
    sequence();

sequence({H,N}) ->
    (H bsl N) bor (sequence() band (1 bsl N - 1)).

%% ---------------------------------------------------------------------------
%% # origin_state_id/0
%% ---------------------------------------------------------------------------

%% 3588, 8.16:
%%
%%    The Origin-State-Id AVP (AVP Code 278), of type Unsigned32, is a
%%    monotonically increasing value that is advanced whenever a Diameter
%%    entity restarts with loss of previous state, for example upon reboot.
%%    Origin-State-Id MAY be included in any Diameter message, including
%%    CER.
%%
%%    A Diameter entity issuing this AVP MUST create a higher value for
%%    this AVP each time its state is reset.  A Diameter entity MAY set
%%    Origin-State-Id to the time of startup, or it MAY use an incrementing
%%    counter retained in non-volatile memory across restarts.

-spec origin_state_id()
   -> diameter:'Unsigned32'().

origin_state_id() ->
    ets:lookup_element(diameter_sequence, origin_state_id, 2).

%% ---------------------------------------------------------------------------
%% # session_id/1
%% ---------------------------------------------------------------------------

%% 3588, 8.8:
%%
%%    The Session-Id MUST begin with the sender's identity encoded in the
%%    DiameterIdentity type (see Section 4.4).  The remainder of the
%%    Session-Id is delimited by a ";" character, and MAY be any sequence
%%    that the client can guarantee to be eternally unique; however, the
%%    following format is recommended, (square brackets [] indicate an
%%    optional element):
%%
%%    <DiameterIdentity>;<high 32 bits>;<low 32 bits>[;<optional value>]
%%
%%    <high 32 bits> and <low 32 bits> are decimal representations of the
%%    high and low 32 bits of a monotonically increasing 64-bit value.  The
%%    64-bit value is rendered in two part to simplify formatting by 32-bit
%%    processors.  At startup, the high 32 bits of the 64-bit value MAY be
%%    initialized to the time, and the low 32 bits MAY be initialized to
%%    zero.  This will for practical purposes eliminate the possibility of
%%    overlapping Session-Ids after a reboot, assuming the reboot process
%%    takes longer than a second.  Alternatively, an implementation MAY
%%    keep track of the increasing value in non-volatile memory.
%%
%%    <optional value> is implementation specific but may include a modem's
%%    device Id, a layer 2 address, timestamp, etc.

-spec session_id(diameter:'DiameterIdentity'())
   -> diameter:'OctetString'().
%% Note that Session-Id has type UTF8String and that any OctetString
%% is a UTF8String.

session_id(Host) ->
    Instr = {_Pos = 2, _Incr = 1, _Threshold = ?INT64, _Set = 0},
    N = ets:update_counter(diameter_sequence, session_base, Instr),
    Hi = N bsr 32,
    Lo = N band ?INT32,
    [Host, ";", integer_to_list(Hi),
           ";", integer_to_list(Lo),
           ";", atom_to_list(node())].

%% ---------------------------------------------------------------------------
%% # init/0
%% ---------------------------------------------------------------------------

init() ->
    {Now, Seed} = diameter_lib:seed(),
    random:seed(Seed),
    Time = time32(Now),
    Seq  = (?INT32 band (Time bsl 20)) bor (random:uniform(1 bsl 20) - 1),
    ets:insert(diameter_sequence, [{origin_state_id, Time},
				   {session_base, Time bsl 32},
				   {sequence, Seq}]),
    Time.

%% ---------------------------------------------------------
%% INTERNAL FUNCTIONS
%% ---------------------------------------------------------

%% The minimum value represented by a Time value. (See diameter_types.)
%% 32 bits extends to 2104.
-define(TIME0, 62105714048).  %% {{1968,1,20},{3,14,8}}

time32(Now) ->
    Time = calendar:now_to_universal_time(Now),
    Diff = calendar:datetime_to_gregorian_seconds(Time) - ?TIME0,
    Diff band ?INT32.