Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 1 | % M2UA in accordance with RFC3331 (http://tools.ietf.org/html/rfc3331) |
| 2 | |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 3 | % (C) 2011-2013 by Harald Welte <laforge@gnumonks.org> |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 4 | % |
| 5 | % All Rights Reserved |
| 6 | % |
| 7 | % This program is free software; you can redistribute it and/or modify |
| 8 | % it under the terms of the GNU Affero General Public License as |
| 9 | % published by the Free Software Foundation; either version 3 of the |
| 10 | % License, or (at your option) any later version. |
| 11 | % |
| 12 | % This program is distributed in the hope that it will be useful, |
| 13 | % but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | % GNU General Public License for more details. |
| 16 | % |
| 17 | % You should have received a copy of the GNU Affero General Public License |
| 18 | % along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 19 | |
| 20 | -module(sctp_m2ua). |
| 21 | -author('Harald Welte <laforge@gnumonks.org>'). |
| 22 | -behaviour(sctp_core). |
| 23 | |
| 24 | -include_lib("kernel/include/inet_sctp.hrl"). |
| 25 | -include("osmo_util.hrl"). |
Harald Welte | e58b38f | 2012-05-30 12:05:18 +0200 | [diff] [blame] | 26 | -include("xua.hrl"). |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 27 | -include("m2ua.hrl"). |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 28 | -include("mtp3.hrl"). |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 29 | |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 30 | -define(M2UA_STREAM_USER, 1). |
| 31 | |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 32 | -export([init/1, terminate/3, code_change/4, handle_event/3, handle_info/3]). |
| 33 | |
| 34 | -export([rx_sctp/4, mtp_xfer/2, state_change/3, prim_up/3]). |
| 35 | |
| 36 | -record(m2ua_state, { |
| 37 | asp_pid, |
| 38 | last_bsn_received, |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 39 | last_fsn_sent, |
| 40 | role |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 41 | }). |
| 42 | |
| 43 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 44 | % gen_fsm callbacks |
| 45 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 46 | |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 47 | init([Role]) -> |
| 48 | Fun = fun(Prim, Args) -> asp_prim_to_user(Prim, Args) end, |
| 49 | AsPid = undefined, % FIXME |
| 50 | % we use sua_asp module, as m2ua has no difference here |
| 51 | {ok, Asp} = gen_fsm:start_link(xua_asp_fsm, [AsPid, sua_asp, [], Fun, [self()], self(), Role], [{debug, [trace]}]), |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 52 | {ok, #m2ua_state{last_bsn_received=16#ffffff, |
| 53 | last_fsn_sent=16#ffffff, asp_pid=Asp, role=Role}}. |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 54 | |
| 55 | terminate(Reason, _State, _LoopDat) -> |
| 56 | io:format("Terminating ~p (Reason ~p)~n", [?MODULE, Reason]), |
| 57 | ok. |
| 58 | |
| 59 | code_change(_OldVsn, _State, LoopDat, _Extra) -> |
| 60 | {ok, LoopDat}. |
| 61 | |
| 62 | handle_event(_Event, State, LoopDat) -> |
| 63 | {next_state, State, LoopDat}. |
| 64 | |
| 65 | handle_info(_Info, State, LoopDat) -> |
| 66 | {next_state, State, LoopDat}. |
| 67 | |
| 68 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 69 | % sctp_core callbacks |
| 70 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 71 | |
| 72 | prim_up(#primitive{subsystem='M', gen_name = 'SCTP_ESTABLISH', spec_name = confirm}, State, LoopDat) -> |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 73 | % confirmation in case of active/connect mode |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 74 | Asp = LoopDat#m2ua_state.asp_pid, |
| 75 | gen_fsm:send_event(Asp, osmo_util:make_prim('M','ASP_UP',request)), |
| 76 | {ignore, LoopDat}; |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 77 | prim_up(#primitive{subsystem='M', gen_name = 'SCTP_ESTABLISH', spec_name = indication}, State, LoopDat) -> |
| 78 | % indication in case of passive/listen mode |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 79 | {ignore, LoopDat}; |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 80 | prim_up(#primitive{subsystem='M', gen_name = 'ASP_UP', spec_name = confirm}, State, LoopDat) -> |
Harald Welte | 4540ecd | 2013-07-27 15:08:53 +0800 | [diff] [blame] | 81 | % confirmation in case of active/connect mode |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 82 | Asp = LoopDat#m2ua_state.asp_pid, |
Harald Welte | 7ccc15e | 2013-08-27 10:27:52 +0200 | [diff] [blame] | 83 | % override mode, interface ID 1. FIXME: user-specify interface ID(s) |
| 84 | Pars = [{?M2UA_P_COM_TRAF_MODE_T, {4, 1}}, {?M2UA_P_COM_INTF_ID_INT, {4, 0}}], |
| 85 | gen_fsm:send_event(Asp, osmo_util:make_prim('M','ASP_ACTIVE',request,Pars)), |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 86 | {ignore, LoopDat}; |
Harald Welte | 287a3fd | 2013-08-27 12:14:07 +0200 | [diff] [blame^] | 87 | prim_up(#primitive{subsystem='M', gen_name = 'ASP_ACTIVE', spec_name = confirm}, State, LoopDat) -> |
| 88 | % FIXME: start a timer or have a separate FSM about it! |
| 89 | M = #xua_msg{version = 1, msg_class = ?M2UA_MSGC_MAUP, |
| 90 | msg_type = ?M2UA_MAUP_MSGT_EST_REQ, |
| 91 | payload = [{?M2UA_P_COM_INTF_ID_INT, {4, 0}}]}, |
| 92 | LoopDat2 = mtp_xfer(M, LoopDat), |
| 93 | {ignore, LoopDat2}; |
| 94 | |
| 95 | |
Harald Welte | 4540ecd | 2013-07-27 15:08:53 +0800 | [diff] [blame] | 96 | |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 97 | prim_up(Prim, State, LoopDat) -> |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 98 | % default: forward all primitives to the user |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 99 | {ok, Prim, LoopDat}. |
| 100 | |
| 101 | |
| 102 | % sctp_core indicates that we have received some data... |
| 103 | rx_sctp(#sctp_sndrcvinfo{ppid = ?M2UA_PPID}, Data, State, LoopDat) -> |
| 104 | Asp = LoopDat#m2ua_state.asp_pid, |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 105 | M2ua = xua_codec:parse_msg(Data), |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 106 | % FIXME: check sequenc number linearity |
| 107 | case M2ua of |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 108 | #xua_msg{msg_class = ?M2UA_MSGC_ASPSM} -> |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 109 | gen_fsm:send_event(Asp, M2ua), |
| 110 | {ignore, LoopDat}; |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 111 | #xua_msg{msg_class = ?M2UA_MSGC_ASPTM} -> |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 112 | gen_fsm:send_event(Asp, M2ua), |
| 113 | {ignore, LoopDat}; |
Harald Welte | e58b38f | 2012-05-30 12:05:18 +0200 | [diff] [blame] | 114 | #xua_msg{msg_class = ?M2UA_MSGC_MAUP, |
Harald Welte | e58b38f | 2012-05-30 12:05:18 +0200 | [diff] [blame] | 115 | msg_type = ?M2UA_MAUP_MSGT_CONG_IND} -> |
| 116 | % FIXME |
| 117 | error_logger:error_report(["unimplemented message", |
| 118 | {msg_type, "CONG_IND"}]), |
| 119 | {ignore, LoopDat}; |
| 120 | #xua_msg{msg_class = ?M2UA_MSGC_MAUP, |
| 121 | msg_type = ?M2UA_MAUP_MSGT_DATA_RETR_REQ} -> |
| 122 | % FIXME |
| 123 | error_logger:error_report(["unimplemented message", |
| 124 | {msg_type, "RETR_REQ"}]), |
| 125 | {ignore, LoopDat}; |
| 126 | #xua_msg{msg_class = ?M2UA_MSGC_MAUP, |
| 127 | msg_type = ?M2UA_MAUP_MSGT_DATA} -> |
Harald Welte | 287a3fd | 2013-08-27 12:14:07 +0200 | [diff] [blame^] | 128 | {_Len, M3bin} = proplists:get_value(?M2UA_P_M2UA_DATA1, M2ua#xua_msg.payload), |
| 129 | Mtp3 = mtp3_codec:parse_mtp3_msg(M3bin), |
Harald Welte | e58b38f | 2012-05-30 12:05:18 +0200 | [diff] [blame] | 130 | Prim = osmo_util:make_prim('MTP','TRANSFER',indication, Mtp3), |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 131 | {ok, Prim, LoopDat}; |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 132 | _ -> |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 133 | rx_sctp(M2ua, State, LoopDat) |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 134 | end. |
| 135 | |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 136 | % SG side |
| 137 | rx_sctp(#xua_msg{msg_class = ?M2UA_MSGC_MAUP, |
| 138 | msg_type = ?M2UA_MAUP_MSGT_EST_REQ}, State, |
| 139 | LoopDat = #m2ua_state{role=sg}) -> |
| 140 | % FIXME: respond with M2UA_MAUP_MSGT_EST_CONF |
| 141 | error_logger:error_report(["unimplemented message", |
| 142 | {msg_type, "EST_REQ"}]), |
| 143 | {ignore, LoopDat}; |
| 144 | rx_sctp(#xua_msg{msg_class = ?M2UA_MSGC_MAUP, |
| 145 | msg_type = ?M2UA_MAUP_MSGT_REL_REQ}, State, |
| 146 | LoopDat = #m2ua_state{role=sg}) -> |
| 147 | % FIXME: respond with M2UA_MAUP_MSGT_REL_CONF |
| 148 | error_logger:error_report(["unimplemented message", |
| 149 | {msg_type, "REL_REQ"}]), |
| 150 | {ignore, LoopDat}; |
| 151 | |
| 152 | rx_sctp(M2ua = #xua_msg{msg_class = ?M2UA_MSGC_MAUP, |
| 153 | msg_type = ?M2UA_MAUP_MSGT_STATE_REQ}, State, |
| 154 | LoopDat = #m2ua_state{role=sg}) -> |
| 155 | handle_m2ua_state_req(M2ua), |
| 156 | {ignore, LoopDat}; |
| 157 | |
| 158 | % ASP side |
| 159 | rx_sctp(#xua_msg{msg_class = ?M2UA_MSGC_MAUP, |
| 160 | msg_type = ?M2UA_MAUP_MSGT_REL_CONF}, State, |
| 161 | LoopDat = #m2ua_state{role=asp}) -> |
| 162 | error_logger:error_report(["unimplemented message", |
| 163 | {msg_type, "REL_CONF"}]), |
| 164 | {ignore, LoopDat}; |
| 165 | |
| 166 | rx_sctp(#xua_msg{msg_class = ?M2UA_MSGC_MAUP, |
| 167 | msg_type = ?M2UA_MAUP_MSGT_EST_CONF}, State, |
| 168 | LoopDat = #m2ua_state{role=asp}) -> |
| 169 | error_logger:error_report(["unimplemented message", |
| 170 | {msg_type, "EST_CONF"}]), |
| 171 | {ignore, LoopDat}; |
| 172 | |
| 173 | rx_sctp(M2ua = #xua_msg{}, State, LoopDat) -> |
| 174 | % do something with link rel msgs |
| 175 | io:format("M2UA Unknown message ~p in state ~p~n", [M2ua, State]), |
| 176 | {ignore, LoopDat}. |
| 177 | |
| 178 | |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 179 | % MTP-TRANSFER.req has arrived at sctp_core, encapsulate+tx it |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 180 | mtp_xfer(M2ua, LoopDat) when is_record(M2ua, xua_msg) -> |
| 181 | M2uaBin = xua_codec:encode_msg(M2ua), |
| 182 | tx_sctp(?M2UA_STREAM_USER, M2uaBin), |
| 183 | LoopDat; |
| 184 | |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 185 | mtp_xfer(Mtp3, LoopDat) when is_record(Mtp3, mtp3_msg) -> |
| 186 | MsgBin = mtp3_codec:encode_mtp3_msg(Mtp3), |
| 187 | mtp_xfer(MsgBin, LoopDat); |
| 188 | |
| 189 | mtp_xfer(Mtp3bin, LoopDat) when is_binary(Mtp3bin) -> |
| 190 | M2ua = #xua_msg{version = 1, |
| 191 | msg_class = ?M2UA_MSGC_MAUP, |
Harald Welte | e58b38f | 2012-05-30 12:05:18 +0200 | [diff] [blame] | 192 | msg_type = ?M2UA_MAUP_MSGT_DATA, |
Harald Welte | cf74df5 | 2013-08-26 17:29:28 +0200 | [diff] [blame] | 193 | payload = [{?M2UA_P_COM_INTF_ID_INT, {4, 0}}, |
| 194 | {?M2UA_P_M2UA_DATA1, {byte_size(Mtp3bin), Mtp3bin}}]}, |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 195 | mtp_xfer(M2ua, LoopDat). |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 196 | |
| 197 | state_change(_, established, LoopDat) -> |
| 198 | % emulate a 'start' from LSC |
| 199 | %gen_fsm:send_event(LoopDat#m2pa_state.lsc_pid, start), |
| 200 | LoopDat; |
| 201 | state_change(established, _, LoopDat) -> |
| 202 | %gen_fsm:send_event(LoopDat#m2pa_state.lsc_pid, link_failure), |
| 203 | LoopDat; |
| 204 | state_change(_, _, LoopDat) -> |
| 205 | LoopDat. |
| 206 | |
Harald Welte | e58b38f | 2012-05-30 12:05:18 +0200 | [diff] [blame] | 207 | handle_m2ua_state_req(M2ua = #xua_msg{payload = Payload}) -> |
| 208 | {?M2UA_P_MAUP_STATE, State} = lists:keyfind(?M2UA_P_MAUP_STATE, 1, Payload), |
| 209 | % FIXME handle_m2ua_state_req(State). |
| 210 | % LOP_SET/CLEAR, EMER_SET/CLEAR, FLUSH_BUFFERSm CONTINUE, CLEAR_RTB, AUDIT, CONG* |
| 211 | % FIXME: respond with M2UA_MAUP_MSGT_STATE_CONF |
| 212 | error_logger:error_report(["unimplemented message", |
| 213 | {msg_type, "STATE_REQ"}]), |
| 214 | true. |
Harald Welte | ee7964c | 2012-05-07 23:55:02 +0200 | [diff] [blame] | 215 | |
| 216 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 217 | % helper functions |
| 218 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 219 | |
| 220 | tx_sctp(Stream, Payload) when is_integer(Stream), is_binary(Payload) -> |
| 221 | Param = {Stream, ?M2UA_PPID, Payload}, |
| 222 | % sent to 'ourselves' (behaviour master module) |
| 223 | gen_fsm:send_event(self(), osmo_util:make_prim('SCTP','TRANSFER',request,Param)). |
Harald Welte | 0d8af6b | 2013-07-27 15:02:17 +0800 | [diff] [blame] | 224 | |
| 225 | % callback fun for ASP FMS |
| 226 | asp_prim_to_user(Prim, [SctpPid]) -> |
| 227 | gen_fsm:send_event(SctpPid, Prim). |