1
0
Fork 0

m2ua: Merge the ASP changes from Pharo to GST

This commit is contained in:
Holger Hans Peter Freyther 2013-06-17 15:15:35 +02:00
parent 372c2e0f0b
commit 6f7e0bcf8d
12 changed files with 1477 additions and 36 deletions

View File

@ -45,7 +45,11 @@ UA = \
ua/XUA.st
M2UA = \
m2ua/M2UAConstants.st m2ua/M2UAMSG.st m2ua/M2UATag.st m2ua/M2UAStates.st
m2ua/M2UAConstants.st m2ua/M2UAMSG.st m2ua/M2UATag.st m2ua/M2UAMessages.st \
m2ua/M2UAStates.st m2ua/M2UAAspStateMachine.st \
m2ua/M2UAApplicationServerProcess.st m2ua/M2UALayerManagement.st \
m2ua/M2UAExamples.st m2ua/M2UATerminology.st m2ua/M2UATests.st
OSMO = \
osmo/LogAreaOsmo.st \

28
core/ExtensionsGST.st Normal file
View File

@ -0,0 +1,28 @@
"
(C) 2013 by Holger Hans Peter Freyther
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 Affero 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/>.
"
BlockClosure extend [
value: arg1 value: arg2 value: arg3 value: arg4 [
<category: '*OsmoNetwork'>
"Evaluate the receiver passing arg1, arg2, arg3 and arg4 as the parameters"
<category: 'built ins'>
<primitive: VMpr_BlockClosure_value>
SystemExceptions.WrongArgumentCount signal
]
]

View File

@ -0,0 +1,507 @@
"
(C) 2013 by Holger Hans Peter Freyther
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 Affero 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/>.
"
Object subclass: M2UAApplicationServerProcess [
| socket asp_active_block asp_down_block asp_inactive_block asp_up_block error_block notify_block sctp_confirm_block sctp_released_block sctp_restarted_block sctp_status_block established state t_ack lastMsg on_state_change as_state |
<category: 'OsmoNetwork-M2UA'>
<comment: 'I am a M2UA Application Server Process.
I have an internal state machine and a state and will be used by the
M2UA Layer. I am written for the usage in a Media Gateway Controller
and will also keep information about the Application Server itself.
If I need to be used on a Signalling Gateway (SG) I will need a dedicated
M2UA Application Server class and state machine.
I can currently only manage a single interface. The specification allows
a single ASP to send one ASPActive for one interface at a time.'>
M2UAApplicationServerProcess class >> initWith: aService [
^self new
socketService: aService;
yourself
]
M2UAApplicationServerProcess class >> new [
^super new
initialize;
yourself
]
onError: aBlock [
"M-ERROR indication
Direction: M2UA -> LM
Purpose: ASP or SGP reports that it has received an ERROR
message from its peer."
<category: 'Primitives-LayerManagement'>
error_block := aBlock
]
onNotify: aBlock [
"M-NOTIFY indication
Direction: M2UA -> LM
Purpose: ASP reports that it has received a NOTIFY message
from its peer."
<category: 'Primitives-LayerManagement'>
notify_block := aBlock
]
onSctpEstablished: aBlock [
"M-SCTP_ESTABLISH confirm
Direction: M2UA -> LM
Purpose: ASP confirms to LM that it has established an SCTP association with an SGP."
<category: 'Primitives-LayerManagement-SCTP'>
sctp_confirm_block := aBlock
]
onSctpReleased: aBlock [
"M-SCTP_RELEASE confirm
Direction: M2UA -> LM
Purpose: ASP confirms to LM that it has released SCTP association with SGP."
<category: 'Primitives-LayerManagement-SCTP'>
sctp_released_block := aBlock
]
onSctpRestarted: aBlock [
"M-SCTP_RELEASE indication
Direction: M2UA -> LM
Purpose: SGP informs LM that ASP has released an SCTP association."
<category: 'Primitives-LayerManagement-SCTP'>
sctp_restarted_block := aBlock
]
onSctpStatus: aBlock [
"M-SCTP_STATUS indication
Direction: M2UA -> LM
Purpose: M2UA reports status of SCTP association."
<category: 'Primitives-LayerManagement-SCTP'>
sctp_status_block := aBlock
]
sctpEstablish [
"M-SCTP_ESTABLISH request
Direction: LM -> M2UA
Purpose: LM requests ASP to establish an SCTP association with an SGP."
<category: 'Primitives-LayerManagement-SCTP'>
established := false.
socket stop.
socket start
]
sctpRelease [
"M-SCTP_RELEASE request
Direction: LM -> M2UA
Purpose: LM requests ASP to release an SCTP association with SGP."
<category: 'Primitives-LayerManagement-SCTP'>
established := false.
socket stop.
t_ack ifNotNil: [t_ack cancel]
]
sctpStatusRequest [
"M-SCTP_STATUS request
Direction: LM -> M2UA
Purpose: LM requests M2UA to report status of SCTP association."
<category: 'Primitives-LayerManagement-SCTP'>
self notYetImplemented
]
aspActive [
<category: 'Primitives-LayerManagemennt-ASP'>
"M-ASP_ACTIVE request
Direction: LM -> M2UA
Purpose: LM requests ASP to send an ASP ACTIVE message to the SGP."
| msg |
self checkNextState: M2UAAspStateActive.
msg := M2UAMSG new
class: M2UAConstants clsASPTM;
msgType: M2UAConstants asptmActiv;
addTag: self createIdentIntTag;
addTag: self createInfoTag;
yourself.
self send: msg
]
aspDown [
<category: 'Primitives-LayerManagemennt-ASP'>
"M-ASP_DOWN request
Direction: LM -> M2UA
Purpose: LM requests ASP to stop its operation and send an ASP DOWN
message to the SGP."
| msg |
self checkNextState: M2UAAspStateDown.
msg := M2UAMSG new
class: M2UAConstants clsASPSM;
msgType: M2UAConstants aspsmDown;
addTag: self createAspIdentTag;
addTag: self createInfoTag;
yourself.
self send: msg
]
aspInactive [
<category: 'Primitives-LayerManagemennt-ASP'>
"M-ASP_INACTIVE request
Direction: LM -> M2UA
Purpose: LM requests ASP to send an ASP INACTIVE message to the SGP."
| msg |
self checkNextState: M2UAAspStateInactive.
msg := M2UAMSG new
class: M2UAConstants clsASPTM;
msgType: M2UAConstants asptmInactiv;
addTag: self createIdentIntTag;
addTag: self createInfoTag;
yourself.
self send: msg
]
aspUp [
<category: 'Primitives-LayerManagemennt-ASP'>
"M-ASP_UP request
Direction: LM -> M2UA
Purpose: LM requests ASP to start its operation and send an ASP UP
message to the SGP."
| msg |
self checkNextState: M2UAAspStateInactive.
msg := M2UAMSG new
class: M2UAConstants clsASPSM;
msgType: M2UAConstants aspsmUp;
addTag: self createAspIdentTag;
addTag: self createInfoTag;
yourself.
self send: msg
]
onAspActive: aBlock [
"M-ASP_ACTIVE confirm
Direction: M2UA -> LM
Purpose: ASP reports that is has received an ASP ACTIVE
Acknowledgment message from the SGP."
<category: 'Primitives-LayerManagemennt-ASP'>
asp_active_block := aBlock
]
onAspDown: aBlock [
"M-ASP_DOWN confirm
Direction: M2UA -> LM
Purpose: ASP reports that is has received an ASP DOWN Acknowledgment
message from the SGP."
<category: 'Primitives-LayerManagemennt-ASP'>
asp_down_block := aBlock
]
onAspInactive: aBlock [
"M-ASP_INACTIVE confirm
Direction: M2UA -> LM
Purpose: ASP reports that is has received an ASP INACTIVE
Acknowledgment message from the SGP."
<category: 'Primitives-LayerManagemennt-ASP'>
asp_inactive_block := aBlock
]
onAspUp: aBlock [
"M-ASP_UP confirm
Direction: M2UA -> LM
Purpose: ASP reports that it has received an ASP UP Acknowledgment
message from the SGP."
<category: 'Primitives-LayerManagemennt-ASP'>
asp_up_block := aBlock
]
onStateChange: aBlock [
"A generic callback for all state changes"
<category: 'Primitives-LayerManagemennt-ASP'>
on_state_change := aBlock
]
deregisterLinkKey [
"M-LINK_KEY_DEREG Request
Direction: LM -> M2UA
Purpose: LM requests ASP to de-register Link Key with SG by sending
DEREG REQ message."
<category: 'Primitives-LayerManagement-LinkKey'>
self notYetImplemented
]
onLinkKeyDeregistered: aBlock [
"M-LINK_KEY_DEREG Confirm
Direction: M2UA -> LM
Purpose: ASP reports to LM that it has successfully received a
DEREG RSP message from SG."
<category: 'Primitives-LayerManagement-LinkKey'>
self notYetImplemented
]
onLinkKeyRegistered: aBlock [
"M-LINK_KEY_REG Confirm
Direction: M2UA -> LM
Purpose: ASP reports to LM that it has successfully received a REG
RSP message from SG."
<category: 'Primitives-LayerManagement-LinkKey'>
self notYetImplemented
]
registerLinkKey [
"M-LINK_KEY_REG Request
Direction: LM -> M2UA
Purpose: LM requests ASP to register Link Key with SG by sending REG
REQ message."
<category: 'Primitives-LayerManagement-LinkKey'>
self notYetImplemented
]
hostname: aHostname port: aPort [
"Select the SCTP hostname/port for the SG to connect to"
<category: 'configuration'>
socket
hostname: aHostname;
port: aPort
]
createAspIdentTag [
<category: 'm2ua-tags'>
^M2UATag initWith: M2UAConstants tagAspIdent data: #(1 2 3 4)
]
createIdentIntTag [
<category: 'm2ua-tags'>
^M2UATag initWith: M2UAConstants tagIdentInt data: #(0 0 0 0)
]
createInfoTag [
<category: 'm2ua-tags'>
^M2UATag initWith: M2UAConstants tagInfo
data: 'Hello from Smalltalk' asByteArray
]
callNotification: aBlock [
"Inform the generic method first, then all the others"
<category: 'private'>
on_state_change ifNotNil: [on_state_change value].
aBlock ifNotNil: [aBlock value]
]
checkNextState: nextState [
"Check if nextState and state are compatible and if not
throw an exception. TODO:"
<category: 'private'>
self state = nextState
ifTrue:
[^self error: ('M2UA ASP already in state <1p>' expandMacrosWith: state)].
(self state nextPossibleStates includes: nextState)
ifFalse:
[^self error: ('M2UA ASP illegal state transition from <1p> to <2p>.'
expandMacrosWith: state
with: nextState)]
]
dispatchData: aByteArray [
<category: 'private'>
| msg |
msg := M2UAMSG parseToClass: aByteArray.
msg dispatchOnAsp: self
]
dispatchNotification: aBlock [
<category: 'private'>
aBlock value
]
internalReset [
<category: 'private'>
self socketService: socket
]
moveToState: newState [
<category: 'private'>
((state nextPossibleStates includes: newState) or: [state = newState])
ifFalse:
[^self error: ('M2UA ASP Illegal state transition from <1p> to <2p>'
expandMacrosWith: state
with: newState)].
"TODO: general on entry, on exit"
state := newState
]
sctpConnected [
<category: 'private'>
"The connect was issued."
| wasEstablished |
wasEstablished := established.
established := true.
state := M2UAAspStateDown.
t_ack ifNotNil: [t_ack cancel].
wasEstablished = true
ifTrue: [sctp_confirm_block ifNotNil: [sctp_confirm_block value]]
ifFalse: [sctp_restarted_block ifNotNil: [sctp_restarted_block value]]
]
sctpReleased [
"The SCTP connection has been released."
<category: 'private'>
self moveToState: M2UAAspStateDown.
established = true ifFalse: [^self].
sctp_released_block ifNotNil: [sctp_released_block value]
]
send: aMsg [
"Forget about what we did before"
<category: 'private'>
t_ack ifNotNil: [t_ack cancel].
t_ack := TimerScheduler instance scheduleInSeconds: 2
block:
["Re-send the message"
self logNotice: ('<1p>:<2p> Sending message has timed out'
expandMacrosWith: socket hostname
with: socket port)
area: #m2ua.
self send: aMsg].
socket nextPut: aMsg toMessage asByteArray
]
initialize [
<category: 'creation'>
state := M2UAAspStateDown
]
socketService: aService [
<category: 'creation'>
socket := aService.
socket
onSctpConnect: [self sctpConnected];
onSctpReleased: [self sctpReleased];
onSctpData:
[:stream :assoc :ppid :data |
ppid = 2
ifFalse:
[^self logNotice: 'M2UAApplicationServerProcess expecting PPID 2.'
area: #m2ua].
self dispatchData: data]
]
handleAspActiveAck: aMsg [
<category: 'dispatch'>
t_ack cancel.
self moveToState: M2UAAspStateActive.
self callNotification: asp_active_block
]
handleAspDownAck: aMsg [
<category: 'dispatch'>
t_ack cancel.
as_state := nil.
self moveToState: M2UAAspStateDown.
self callNotification: asp_down_block
]
handleAspInactiveAck: aMsg [
<category: 'dispatch'>
t_ack cancel.
as_state := nil.
self moveToState: M2UAAspStateInactive.
self callNotification: asp_inactive_block
]
handleAspUpAck: aMsg [
<category: 'dispatch'>
t_ack cancel.
self moveToState: M2UAAspStateInactive.
self callNotification: asp_inactive_block
]
handleError: aMsg [
"Cancel pending operations.. because something went wrong"
<category: 'dispatch'>
t_ack cancel.
error_block ifNotNil: [error_block value: aMsg]
]
handleNotify: aMsg [
<category: 'dispatch'>
"Extract the status"
| tag type ident |
tag := aMsg findTag: M2UAConstants tagStatus.
tag ifNil: [^self].
type := (tag data ushortAt: 1) swap16.
ident := (tag data ushortAt: 3) swap16.
type = M2UAConstants ntfyKindStateChange ifTrue: [as_state := ident].
"Inform our user about it"
notify_block ifNotNil: [notify_block value: type value: ident]
]
handleUnknownMessage: aMsg [
"We got something we don't know. ignore it for now."
<category: 'dispatch'>
]
isASActive [
<category: 'status'>
^as_state = M2UAConstants ntfyStateASActive
]
isASInactive [
<category: 'status'>
^as_state = M2UAConstants ntfyStateASInactive
]
isASPending [
<category: 'status'>
^as_state = M2UAConstants ntfyStateASPending
]
state [
<category: 'accessing'>
^state
]
]

106
m2ua/M2UAAspStateMachine.st Normal file
View File

@ -0,0 +1,106 @@
"
(C) 2013 by Holger Hans Peter Freyther
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 Affero 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/>.
"
Object subclass: M2UAAspStateMachine [
| state |
<category: 'OsmoNetwork-M2UA-States'>
<comment: 'I am the Application Server Process State machine. An
pplication Server Process will create me to manage the state. My state
machine is driven by calling the selectors from the events protocol.
If you ask for an illegal state transition a DNU will be raised. Ath
this point you should probably reset what you are doing and do proper
error reporting.
This class is currently not used!'>
M2UAAspStateMachine class >> initialState [
^M2UAAspStateDown
]
M2UAAspStateMachine class >> new [
^(self basicNew)
initialize;
yourself
]
entered: aState [
aState entered
"TODO notify users of the machine"
]
initialize [
state := self class initialState on: self
]
left: aState [
aState left
"TODO notify users of the machine"
]
moveToState: aNewState [
| oldState |
oldState := state.
state := (aNewState new)
machine: self;
yourself.
self left: oldState.
self entered: state
]
state [
^state class
]
aspActive: anEvent [
<category: 'events'>
state onAspActive: anEvent
]
aspDown: anEvent [
<category: 'events'>
state onAspDown: anEvent
]
aspInactive: anEvent [
<category: 'events'>
state onAspInactive: anEvent
]
aspUp: anEvent [
<category: 'events'>
state onAspUp: anEvent
]
otherAspInAsOverrides: anEvent [
<category: 'events'>
state onOtherAspInAsOverrides: anEvent
]
sctpCdi: anEvent [
<category: 'events'>
state onSctpCdi: anEvent
]
sctpRi: anEvent [
<category: 'events'>
state onSctpRi: anEvent
]
]

42
m2ua/M2UAExamples.st Normal file
View File

@ -0,0 +1,42 @@
"
(C) 2013 by Holger Hans Peter Freyther
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 Affero 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/>.
"
Object subclass: M2UAExamples [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
createAsp [
"Create a SCTP network service"
| service asp manager |
service := SCTPNetworkService new
hostname: 'localhost';
port: 2904;
yourself.
"Create the ASP"
asp := M2UAApplicationServerProcess initWith: service.
"Create a Layer Management (LM) and start it"
manager := M2UALayerManagement new
applicationServerProcess: asp;
targetState: M2UAAspStateActive;
yourself.
manager manage
]
]

127
m2ua/M2UALayerManagement.st Normal file
View File

@ -0,0 +1,127 @@
"
(C) 2013 by Holger Hans Peter Freyther
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 Affero 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/>.
"
Object subclass: M2UALayerManagement [
| targetState managedProcess |
<category: 'OsmoNetwork-M2UA'>
<comment: 'I am taking the LayerManagement control for an M2UAApplicationServiceProcess.
Currently you can tell me the ASP state this class should be in
and I will react to to the events from the ASP.'>
applicationServerProcess: aProcess [
<category: 'creation'>
managedProcess := aProcess.
managedProcess
onSctpEstablished: [self sctpEstablished];
onSctpRestarted: [self sctpEstablished];
onError: [:msg | self m2uaError: msg];
onNotify: [:type :ident | self m2uaNotify: type ident: ident];
onAspActive: [self m2uaActive];
onAspInactive: [self m2uaInactive];
onAspDown: [self m2uaDown];
onAspUp: [self m2uaUp]
]
manage [
"I begin to manage the process."
<category: 'creation'>
managedProcess
sctpRelease;
sctpEstablish
]
targetState: aState [
"Use the M2UAAspState subclasses for the states"
<category: 'creation'>
targetState := aState
]
applicationServerProcess [
<category: 'accessing'>
^managedProcess
]
m2uaActive [
"E.g if the target state is already reached"
<category: 'as-process-callbacks'>
managedProcess state = targetState ifTrue: [^self targetReached].
targetState = M2UAAspStateInactive
ifTrue: [managedProcess aspInactive]
ifFalse: [managedProcess aspDown]
]
m2uaDown [
"E.g if the target state is already reached"
<category: 'as-process-callbacks'>
managedProcess state = targetState ifTrue: [^self targetReached].
"There is only one way forward"
managedProcess aspUp
]
m2uaError: aMsg [
<category: 'as-process-callbacks'>
self logNotice: 'M2UA Error.' area: #m2ua
]
m2uaInactive [
"E.g if the target state is already reached"
<category: 'as-process-callbacks'>
managedProcess state = targetState ifTrue: [^self targetReached].
targetState = M2UAAspStateActive
ifTrue: [managedProcess aspActive]
ifFalse: [managedProcess aspDown]
]
m2uaNotify: type ident: ident [
"TODO: Check the type/ident"
<category: 'as-process-callbacks'>
]
m2uaUp [
"E.g if the target state is already reached"
<category: 'as-process-callbacks'>
managedProcess state = targetState ifTrue: [^self targetReached].
targetState = M2UAAspStateActive
ifTrue: [managedProcess aspActive]
ifFalse: [managedProcess aspInactive]
]
sctpEstablished [
"E.g if the target state is already reached"
<category: 'as-process-callbacks'>
managedProcess state = targetState ifTrue: [^self].
"There is only one way forward"
managedProcess aspUp
]
targetReached [
]
]

View File

@ -57,6 +57,41 @@ struct m2ua_parameter_hdr {
yourself.
]
M2UAMSG class >> copyFrom: aMsg [
<category: 'parsing'>
^ self new
msgClass: aMsg msgClass;
msgType: aMsg msgType;
tags: aMsg tags;
yourself
]
M2UAMSG class >> parseToClass: aMsg [
<category: 'parsing'>
"This will attempt to parse the message into one of the
available subclasses."
| rawMsg msgClasses |
rawMsg := self parseFrom: aMsg.
"A simple class based lookup"
msgClasses :=
{M2UAASPSMMessage.
M2UAASPTMMessage.
M2UAASPMGMTMessage}.
msgClasses do:
[:msgClass |
rawMsg msgClass = msgClass messageClass
ifTrue:
[msgClass allSubclassesDo: [:class |
class messageTag = rawMsg msgType
ifTrue: [^class copyFrom: rawMsg]]]].
^self error: ('Unknown message class (<1p>) or message type (<2p>)'
expandMacrosWith: rawMsg msgClass
with: rawMsg msgType)
]
msgClass [
<category: 'accessing'>
^ msg_class
@ -67,6 +102,13 @@ struct m2ua_parameter_hdr {
^ msg_type
]
findTag: aTag [
"I find a tag with a tag identifier"
<category: 'accessing'>
^self findTag: aTag ifAbsent: [nil]
]
findTag: aTag ifAbsent: aBlock [
"I find a tag with a tag identifier"
<category: 'accessing'>
@ -85,42 +127,61 @@ struct m2ua_parameter_hdr {
]
parseFrom: aStream [
| version spare len end |
<category: 'parsing'>
version := aStream next.
version = M2UAConstants version ifFalse: [
self logError:
('M2UA version is wrong <1p>.' expandMacrosWith: version) area: #m2ua.
self error: ('M2UA version is wrong <1p>.' expandMacrosWith: version).
].
spare := aStream next.
spare = M2UAConstants spare ifFalse: [
self logError: ('M2UA spare is wrong <1p>.' expandMacrosWith: spare) area: #m2ua.
self error: ('M2UA spare is wrong <1p>.' expandMacrosWith: spare).
].
msg_class := aStream next.
msg_type := aStream next.
len := ((aStream next: 4) uintAt: 1) swap32.
aStream size - aStream position < (len - 8) ifTrue: [
self logError: ('M2UA length is not plausible <1p> <2p>.'
expandMacrosWith: len with: aStream size - aStream position)
area: #m2ua.
self error: ('M2UA length is not plausible <1p> <2p>.'
expandMacrosWith: len with: aStream size - aStream position).
].
tags := OrderedCollection new.
end := aStream position + len - 8.
[aStream position < end] whileTrue: [
tags add: (M2UATag fromStream: aStream)
].
<category: 'parsing'>
| len |
self parseVersion: aStream.
self parseSpare: aStream.
msg_class := aStream next.
msg_type := aStream next.
len := self parseLength: aStream.
tags := self parseTags: aStream to: aStream position + len - 8
]
parseLength: aStream [
<category: 'parsing'>
| len |
len := ((aStream next: 4) uintAt: 1) swap32.
aStream size - aStream position < (len - 8)
ifTrue:
[self
logError: ('M2UA length is not plausible <1p> <2p>.' expandMacrosWith: len
with: aStream size - aStream position)
area: #m2ua.
self
error: ('M2UA length is not plausible <1p> <2p>.' expandMacrosWith: len
with: aStream size - aStream position)].
^len
]
parseSpare: aStream [
<category: 'parsing'>
| spare |
spare := aStream next.
spare = M2UAConstants spare
ifFalse:
[self logError: ('M2UA spare is wrong <1p>.' expandMacrosWith: spare)
area: #m2ua.
self error: ('M2UA spare is wrong <1p>.' expandMacrosWith: spare)]
]
parseTags: aStream to: end [
<category: 'parsing'>
tags := OrderedCollection new.
[aStream position < end]
whileTrue: [tags add: (M2UATag fromStream: aStream)].
^tags
]
parseVersion: aStream [
<category: 'parsing'>
| version |
version := aStream next.
version = M2UAConstants version
ifFalse:
[self logError: ('M2UA version is wrong <1p>.' expandMacrosWith: version)
area: #m2ua.
self error: ('M2UA version is wrong <1p>.' expandMacrosWith: version)]
]
addTag: aTag [
<category: 'encoding'>
self tags add: aTag.
@ -143,5 +204,30 @@ struct m2ua_parameter_hdr {
aMsg putLen32: tag_data size + 8.
aMsg putByteArray: tag_data.
]
class: aClass [
<category: 'creation'>
msg_class := aClass
]
msgClass: aClass [
<category: 'creation'>
self class: aClass
]
msgType: aType [
<category: 'creation'>
msg_type := aType
]
tags: aTags [
<category: 'creation'>
tags := aTags
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleUnknownMessage: self
]
]

217
m2ua/M2UAMessages.st Normal file
View File

@ -0,0 +1,217 @@
"
(C) 2011-2013 by Holger Hans Peter Freyther
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 Affero 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/>.
"
M2UAMSG subclass: M2UAASPSMMessage [
<category: 'OsmoNetwork-M2UA'>
<comment: 'Application Server Process State Maintenance (ASPSM) messages'>
M2UAASPSMMessage class >> messageClass [
^M2UAConstants clsASPSM
]
]
M2UAMSG subclass: M2UAASPTMMessage [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAASPTMMessage class >> messageClass [
^M2UAConstants clsASPTM
]
]
M2UAMSG subclass: M2UAASPMGMTMessage [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAASPMGMTMessage class >> messageClass [
^M2UAConstants clsMgmt
]
]
M2UAASPSMMessage subclass: M2UAApplicationServerProcessHeartbeatAck [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessHeartbeatAck class >> messageTag [
^M2UAConstants aspsmBeatAck
]
]
M2UAASPSMMessage subclass: M2UAApplicationServerProcessDown [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessDown class >> messageTag [
^M2UAConstants aspsmDown
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleAspDown: self
]
]
M2UAASPSMMessage subclass: M2UAApplicationServerProcessHeartbeat [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessHeartbeat class >> messageTag [
^M2UAConstants aspsmBeat
]
]
M2UAASPSMMessage subclass: M2UAApplicationServerProcessDownAck [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessDownAck class >> messageTag [
^M2UAConstants aspsmDownAck
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleAspDownAck: self
]
]
M2UAASPSMMessage subclass: M2UAApplicationServerProcessUp [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessUp class >> messageTag [
^M2UAConstants aspsmUp
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleAspUp: self
]
]
M2UAASPTMMessage subclass: M2UAApplicationServerProcessInactiveAck [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessInactiveAck class >> messageTag [
^M2UAConstants asptmInactivAck
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleAspInactiveAck: self
]
]
M2UAASPTMMessage subclass: M2UAApplicationServerProcessActive [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessActive class >> messageTag [
^M2UAConstants asptmActiv
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleAspActive: self
]
]
M2UAASPTMMessage subclass: M2UAApplicationServerProcessInactive [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessInactive class >> messageTag [
^M2UAConstants asptmInactiv
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleAspInactive: self
]
]
M2UAASPMGMTMessage subclass: M2UAApplicationServerProcessNotify [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessNotify class >> messageTag [
^M2UAConstants mgmtNtfy
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleNotify: self
]
]
M2UAASPMGMTMessage subclass: M2UAApplicationServerProcessError [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessError class >> messageTag [
^M2UAConstants mgmtError
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleError: self
]
]
M2UAASPTMMessage subclass: M2UAApplicationServerProcessActiveAck [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessActiveAck class >> messageTag [
^M2UAConstants asptmActivAck
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleAspActiveAck: self
]
]
M2UAASPSMMessage subclass: M2UAApplicationServerProcessUpAck [
<category: 'OsmoNetwork-M2UA'>
<comment: nil>
M2UAApplicationServerProcessUpAck class >> messageTag [
^M2UAConstants aspsmUpAck
]
dispatchOnAsp: anAsp [
<category: 'm2ua-asp-dispatch'>
anAsp handleAspUpAck: self
]
]

View File

@ -42,6 +42,7 @@ STInST.RBProgramNodeVisitor subclass: M2UAStateMachineVisitor [
Object subclass: M2UAStateBase [
| machine |
<category: 'OsmoNetwork-M2UA-States'>
<comment: 'I am the base class of all M2UA state machines. My direct subclasses are state machines and their subclasses are the individual states that make up the statemachine.'>
@ -82,6 +83,31 @@ Object subclass: M2UAStateBase [
nextPutAll: '}';
contents
]
M2UAStateBase class >> on: aMachine [
"Create a new state for a machine"
^self new
machine: aMachine;
yourself
]
entered [
"The state has been entered"
]
left [
"The state has been left"
]
machine: aMachine [
machine := aMachine
]
moveToState: aNewState [
<category: 'transition'>
machine moveToState: aNewState
]
]
@ -147,6 +173,10 @@ M2UAStateBase subclass: M2UAAspState [
<category: 'OsmoNetwork-M2UA-States'>
<comment: 'I am the base class of the ASP State Machine from RFC 3331 on Page 61.'>
M2UAAspState class >> nextPossibleStates [
^self subclassResponsibility
]
]
@ -156,6 +186,10 @@ M2UAAspState subclass: M2UAAspStateActive [
<category: 'OsmoNetwork-M2UA-States'>
<comment: nil>
M2UAAspStateActive class >> nextPossibleStates [
^ {M2UAAspStateInactive. M2UAAspStateDown}
]
onAspDown: anEvent [
<category: 'state-changes'>
self moveToState: M2UAAspStateDown
@ -189,6 +223,10 @@ M2UAAspState subclass: M2UAAspStateDown [
<category: 'OsmoNetwork-M2UA-States'>
<comment: nil>
M2UAAspStateDown class >> nextPossibleStates [
^{M2UAAspStateInactive}
]
onAspUp: anEvent [
<category: 'state-changes'>
^self moveToState: M2UAAspStateInactive
@ -202,6 +240,10 @@ M2UAAspState subclass: M2UAAspStateInactive [
<category: 'OsmoNetwork-M2UA-States'>
<comment: nil>
M2UAAspStateInactive class >> nextPossibleStates [
^ {M2UAAspStateActive. M2UAAspStateDown}
]
onAspActive: anEvent [
<category: 'state-changes'>
^self moveToState: M2UAAspStateActive

44
m2ua/M2UATerminology.st Normal file
View File

@ -0,0 +1,44 @@
"
(C) 2011-2013 by Holger Hans Peter Freyther
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 Affero 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/>.
"
Object subclass: M2UATerminology [
<category: 'OsmoNetwork-M2UA'>
<comment: 'I attempt to help with the terminology for M2UA.
M2UA is defined in IETF RFC 3331 and is actually from a family
of closely related RFCs for M3UA, SUA, M2PA.
The whole idea is that one can adapt the M2UA layer from the classlic
E1/T1 timeslots to the more modern SCTP (SIGTRAN). MTP3 and above will
not notice the difference.
The communication for M2UA is between two systems, both should be
configurable as either a client or server (listening for incoming SCTP
connections).
In general the communication is between a Signalling Gateway
(SG) and a Media Gateway Controller (MGC). In our world the MGC
would is the MSC/HLR/VLR/AuC.
What makes things complicated is the cardinality of systems. There is
an Application Server (AS), this can have multiple Application Server
Processes (ASP) for one or multiple MTP links. While the RFC onlys
says that the SG should the list of ASs in practice both ends need to
do it.'>
]

222
m2ua/M2UATests.st Normal file
View File

@ -0,0 +1,222 @@
"
(C) 2013 by Holger Hans Peter Freyther
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 Affero 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/>.
"
Object subclass: M2UAASMock [
| socket |
<category: 'OsmoNetwork-M2UA-Tests'>
<comment: 'A simple mock'>
socketService: aSocket [
<category: 'creation'>
socket := aSocket
]
handleAspActive: aMsg [
<category: 'dispatch'>
| ret |
ret := M2UAMSG new
msgClass: M2UAConstants clsASPTM;
msgType: M2UAConstants asptmActivAck;
yourself.
socket sendToAsp: ret toMessage asByteArray
]
handleAspDown: aMsg [
<category: 'dispatch'>
| ret |
ret := M2UAMSG new
msgClass: M2UAConstants clsASPSM;
msgType: M2UAConstants aspsmDownAck;
yourself.
socket sendToAsp: ret toMessage asByteArray
]
handleAspInactive: aMsg [
<category: 'dispatch'>
| ret |
ret := M2UAMSG new
msgClass: M2UAConstants clsASPTM;
msgType: M2UAConstants asptmInactivAck;
yourself.
socket sendToAsp: ret toMessage asByteArray
]
handleAspUp: aMsg [
<category: 'dispatch'>
| ret |
ret := M2UAMSG new
msgClass: M2UAConstants clsASPSM;
msgType: M2UAConstants aspsmUpAck;
yourself.
socket sendToAsp: ret toMessage asByteArray
]
onData: aData [
| msg |
msg := M2UAMSG parseToClass: aData.
msg dispatchOnAsp: self
]
]
Object subclass: SCTPNetworkServiceMock [
| on_connect on_released on_data as asp |
<category: 'OsmoNetwork-M2UA-Tests'>
<comment: 'I mock SCTPand directly connect an AS with an ASP.'>
onSctpConnect: aBlock [
<category: 'notification'>
on_connect := aBlock
]
applicationServer: anAs [
<category: 'creation'>
as := anAs
]
applicationServerProcess: anAsp [
<category: 'creation'>
asp := anAsp
]
onSctpData: aBlock [
<category: 'creation'>
on_data := aBlock
]
onSctpReleased: aBlock [
<category: 'creation'>
on_released := aBlock
]
hostname [
<category: 'management'>
^'localhost'
]
port [
<category: 'management'>
^0
]
start [
"Nothing"
<category: 'management'>
on_connect value
]
stop [
<category: 'management'>
on_released value
]
nextPut: aMsg [
as onData: aMsg
]
sendToAsp: aMsg [
on_data
value: nil
value: nil
value: 2
value: aMsg
]
]
TestCase subclass: M2UAApplicationServerProcessTest [
<comment: 'A M2UAApplicationServerProcessTest is a test class for testing the behavior of M2UAApplicationServerProcess'>
<category: 'OsmoNetwork-M2UA-Tests'>
testCreation [
| asp |
asp := M2UAApplicationServerProcess new
onAspActive: [];
onAspDown: [];
onAspInactive: [];
onAspUp: [];
onStateChange: [];
onError: [:msg | ];
onNotify: [:type :ident | ];
onSctpEstablished: [];
onSctpReleased: [];
onSctpRestarted: [];
onSctpStatus: [];
yourself
]
testStateTransitions [
| mock as asp |
mock := SCTPNetworkServiceMock new.
as := M2UAASMock new
socketService: mock;
yourself.
asp := M2UAApplicationServerProcess initWith: mock.
mock
applicationServer: as;
applicationServerProcess: asp.
"This works as the mock will handle this synchronously"
self assert: asp state = M2UAAspStateDown.
asp
sctpEstablish;
aspUp.
self assert: asp state = M2UAAspStateInactive.
"Now bring it down and up again"
asp aspDown.
self assert: asp state = M2UAAspStateDown.
asp
aspUp;
aspActive.
self assert: asp state = M2UAAspStateActive.
asp aspDown.
self assert: asp state = M2UAAspStateDown.
asp
aspUp;
aspActive;
aspInactive.
self assert: asp state = M2UAAspStateInactive.
asp sctpRelease.
self assert: asp state = M2UAAspStateDown
]
]
TestCase subclass: M2UAAspStateMachineTest [
<comment: 'A M2UAAspStateMachineTest is a test class for testing the behavior of M2UAAspStateMachine'>
<category: 'OsmoNetwork-M2UA-Tests'>
testLegalTransitions [
| machine |
machine := M2UAAspStateMachine new.
self assert: machine state = M2UAAspStateDown.
machine aspUp: 'Link is up'.
self assert: machine state = M2UAAspStateInactive.
machine aspActive: 'Active'.
self assert: machine state = M2UAAspStateActive.
machine aspInactive: 'Inactive'.
self assert: machine state = M2UAAspStateInactive.
machine aspActive: 'Active'.
self assert: machine state = M2UAAspStateActive.
machine sctpCdi: 'Connection is gone'.
self assert: machine state = M2UAAspStateDown
]
]

View File

@ -9,6 +9,7 @@
<prereq>Parser</prereq>
<filein>core/Extensions.st</filein>
<filein>core/ExtensionsGST.st</filein>
<filein>core/MessageStructure.st</filein>
<filein>core/MessageBuffer.st</filein>
<filein>core/LogAreas.st</filein>
@ -28,10 +29,21 @@
<filein>sccp/SCCPGlobalTitleTranslation.st</filein>
<filein>mtp3/MTP3Messages.st</filein>
<filein>ua/XUA.st</filein>
<filein>m2ua/M2UAConstants.st</filein>
<filein>m2ua/M2UAStates.st</filein>
<filein>m2ua/M2UATag.st</filein>
<filein>m2ua/M2UAMSG.st</filein>
<filein>m2ua/M2UAMessages.st</filein>
<filein>m2ua/M2UAStates.st</filein>
<filein>m2ua/M2UAAspStateMachine.st</filein>
<filein>m2ua/M2UAApplicationServerProcess.st</filein>
<filein>m2ua/M2UALayerManagement.st</filein>
<filein>m2ua/M2UATerminology.st</filein>
<filein>m2ua/M2UAExamples.st</filein>
<filein>osmo/LogAreaOsmo.st</filein>
<filein>osmo/OsmoUDPSocket.st</filein>
<filein>osmo/OsmoCtrlLogging.st</filein>
@ -48,13 +60,16 @@
<sunit>Osmo.IPAGSTTests</sunit>
<sunit>Osmo.IPAMsgTests</sunit>
<sunit>Osmo.MessageBufferTest</sunit>
<sunit>Osmo.M2UAMSGTests</sunit>
<sunit>Osmo.ISUPGeneratedTest</sunit>
<sunit>Osmo.OsmoUDPSocketTest</sunit>
<sunit>Osmo.TLVDescriptionTest</sunit>
<sunit>Osmo.CtrlGrammarTest</sunit>
<sunit>Osmo.CtrlParserTest</sunit>
<sunit>Osmo.M2UAMSGTests</sunit>
<sunit>Osmo.M2UAApplicationServerProcessTest</sunit>
<sunit>Osmo.M2UAAspStateMachineTest</sunit>
<sunit>Osmo.MTP3LabelTest</sunit>
<sunit>Osmo.MTP3SLTAMSGTest</sunit>
<sunit>Osmo.MTP3SLTMMSGTest</sunit>
@ -66,6 +81,7 @@
<filein>isup/ISUPTests.st</filein>
<filein>ipa/IPATests.st</filein>
<filein>osmo/OsmoCtrlGrammarTest.st</filein>
<filein>m2ua/M2UATests.st</filein>
<filein>mtp3/MTP3MessagesTests.st</filein>
</test>
</package>