%% %% %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): %% %% ;;[;] %% %% and 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. %% %% 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 = now(), random:seed(Now), 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.