Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 1 | % ITU-T Q.71x SCCP UDT stateful masquerading |
| 2 | |
| 3 | % (C) 2011 by Harald Welte <laforge@gnumonks.org> |
| 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/>. |
Harald Welte | 0b452a8 | 2012-04-16 13:17:31 +0200 | [diff] [blame^] | 19 | % |
| 20 | % Additional Permission under GNU AGPL version 3 section 7: |
| 21 | % |
| 22 | % If you modify this Program, or any covered work, by linking or |
| 23 | % combining it with runtime libraries of Erlang/OTP as released by |
| 24 | % Ericsson on http://www.erlang.org (or a modified version of these |
| 25 | % libraries), containing parts covered by the terms of the Erlang Public |
| 26 | % License (http://www.erlang.org/EPLICENSE), the licensors of this |
| 27 | % Program grant you additional permission to convey the resulting work |
| 28 | % without the need to license the runtime libraries of Erlang/OTP under |
| 29 | % the GNU Affero General Public License. Corresponding Source for a |
| 30 | % non-source form of such a combination shall include the source code |
| 31 | % for the parts of the runtime libraries of Erlang/OTP used as well as |
| 32 | % that of the covered work. |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 33 | |
| 34 | -module(sccp_masq). |
| 35 | -author('Harald Welte <laforge@gnumonks.org>'). |
Harald Welte | 889efcf | 2011-02-07 22:34:37 +0100 | [diff] [blame] | 36 | -include_lib("osmo_ss7/include/sccp.hrl"). |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 37 | |
Harald Welte | af3a9fc | 2011-02-25 10:36:43 +0100 | [diff] [blame] | 38 | -export([sccp_masq_msg/3, init/0, reset/0, dump/0, lookup_masq_addr/2]). |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 39 | |
| 40 | -compile([export_all]). |
| 41 | |
| 42 | -record(sccp_masq_rec, { |
| 43 | digits_in, % list of GT digits |
| 44 | digits_out, % list of GT digits |
| 45 | last_access % timestamp of last usage |
| 46 | }). |
| 47 | |
| 48 | % alloc + insert a new masquerade state record in our tables |
| 49 | masq_alloc(DigitsOrig) -> |
Harald Welte | a53a63a | 2012-02-13 22:10:33 +0100 | [diff] [blame] | 50 | {ok, Base} = application:get_env(mgw_nat, sccp_masq_gt_base), |
| 51 | {ok, Max} = application:get_env(mgw_nat, sccp_masq_gt_max), |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 52 | masq_try_alloc(DigitsOrig, Base, Max, 0). |
| 53 | masq_try_alloc(_DigitsOrig, _Base, Max, Offset) when Offset > Max -> |
| 54 | undef; |
| 55 | masq_try_alloc(DigitsOrig, Base, Max, Offset) -> |
| 56 | Try = Base + Offset, |
Harald Welte | 7f789ed | 2011-02-09 22:25:35 +0100 | [diff] [blame] | 57 | TryBin = osmo_util:int2digit_list(Try), |
Harald Welte | 0a4ff60 | 2011-02-24 23:05:01 +0100 | [diff] [blame] | 58 | % try to first allocate the reverse mapping, i.e. where the new |
| 59 | % masqueraded address is the unique criteria for table lookup |
Harald Welte | 3b10578 | 2012-03-29 21:36:15 +0200 | [diff] [blame] | 60 | EtsRet = ets:insert_new(sccp_masq_rev, |
Harald Welte | 0a4ff60 | 2011-02-24 23:05:01 +0100 | [diff] [blame] | 61 | #sccp_masq_rec{digits_in = TryBin, |
| 62 | digits_out = DigitsOrig}), |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 63 | case EtsRet of |
| 64 | false -> |
| 65 | masq_try_alloc(DigitsOrig, Base, Max, Offset+1); |
| 66 | _ -> |
Harald Welte | 3b10578 | 2012-03-29 21:36:15 +0200 | [diff] [blame] | 67 | ets:insert(sccp_masq_orig, |
Harald Welte | 0a4ff60 | 2011-02-24 23:05:01 +0100 | [diff] [blame] | 68 | #sccp_masq_rec{digits_in = DigitsOrig, |
| 69 | digits_out = TryBin}), |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 70 | Try |
| 71 | end. |
| 72 | |
| 73 | % lookup a masqerade state record |
| 74 | lookup_masq_addr(orig, GtDigits) -> |
Harald Welte | 3b10578 | 2012-03-29 21:36:15 +0200 | [diff] [blame] | 75 | case ets:lookup(sccp_masq_orig, GtDigits) of |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 76 | [#sccp_masq_rec{digits_out = DigitsOut}] -> |
| 77 | DigitsOut; |
| 78 | _ -> |
Harald Welte | 42cd3c6 | 2011-02-10 21:03:53 +0100 | [diff] [blame] | 79 | % we do not allocate entries right here |
| 80 | undef |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 81 | end; |
| 82 | lookup_masq_addr(rev, GtDigits) -> |
Harald Welte | 3b10578 | 2012-03-29 21:36:15 +0200 | [diff] [blame] | 83 | case ets:lookup(sccp_masq_rev, GtDigits) of |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 84 | [#sccp_masq_rec{digits_out = DigitsOut}] -> |
| 85 | DigitsOut; |
| 86 | _ -> |
| 87 | % we do not allocate entries in the reverse direction |
| 88 | undef |
| 89 | end. |
| 90 | |
Harald Welte | 42cd3c6 | 2011-02-10 21:03:53 +0100 | [diff] [blame] | 91 | lookup_or_alloc_masq_addr(Dir, GtDigits) -> |
| 92 | case lookup_masq_addr(Dir, GtDigits) of |
| 93 | undef -> |
| 94 | % allocate a new masq GT |
| 95 | masq_alloc(GtDigits); |
| 96 | Addr -> |
| 97 | Addr |
| 98 | end. |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 99 | |
| 100 | % Masquerade the CALLING address in first STP(G-MSC) -> HLR/VLR/MSC dir |
| 101 | mangle_rx_calling(from_stp, Addr = #sccp_addr{global_title = GT}) -> |
| 102 | GtOrig = GT#global_title.phone_number, |
Harald Welte | 42cd3c6 | 2011-02-10 21:03:53 +0100 | [diff] [blame] | 103 | GtReplace = lookup_or_alloc_masq_addr(orig, GtOrig), |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 104 | case GtReplace of |
| 105 | undef -> |
| 106 | io:format("SCCP MASQ: Unable to rewrite in original direction (out of GT addrs?)~n"), |
| 107 | Addr; |
| 108 | _ -> |
| 109 | io:format("SCCP MASQ (STP->MSC) rewrite ~p->~p~n", [GtOrig, GtReplace]), |
| 110 | GTout = GT#global_title{phone_number = GtReplace}, |
| 111 | Addr#sccp_addr{global_title = GTout} |
| 112 | end; |
| 113 | mangle_rx_calling(_From, Addr) -> |
| 114 | Addr. |
| 115 | |
| 116 | mangle_rx_called(from_msc, Addr = #sccp_addr{global_title = GT}) -> |
| 117 | GtOrig = GT#global_title.phone_number, |
| 118 | GtReplace = lookup_masq_addr(rev, GtOrig), |
| 119 | case GtReplace of |
| 120 | undef -> |
| 121 | io:format("SCCP MASQ: Unable to rewrite in original direction (unknown GT ~p)~n", [GT]), |
| 122 | Addr; |
| 123 | _ -> |
| 124 | io:format("SCCP MASQ (MSC->STP) rewrite ~p->~p~n", [GtOrig, GtReplace]), |
| 125 | GTout = GT#global_title{phone_number = GtReplace}, |
| 126 | Addr#sccp_addr{global_title = GTout} |
| 127 | end; |
| 128 | mangle_rx_called(_From, Addr) -> |
| 129 | Addr. |
| 130 | |
| 131 | |
| 132 | sccp_masq_msg(From, ?SCCP_MSGT_UDT, Msg = #sccp_msg{parameters = Opts}) -> |
| 133 | CalledParty = proplists:get_value(called_party_addr, Opts), |
| 134 | CalledPartyNew = mangle_rx_called(From, CalledParty), |
| 135 | CallingParty = proplists:get_value(calling_party_addr, Opts), |
| 136 | CallingPartyNew = mangle_rx_calling(From, CallingParty), |
| 137 | Opts1 = lists:keyreplace(called_party_addr, 1, Opts, |
| 138 | {called_party_addr, CalledPartyNew}), |
| 139 | Opts2 = lists:keyreplace(calling_party_addr, 1, Opts1, |
| 140 | {calling_party_addr, CallingPartyNew}), |
| 141 | Msg#sccp_msg{parameters = Opts2}; |
| 142 | sccp_masq_msg(_From, _MsgType, Msg) -> |
| 143 | Msg. |
| 144 | |
| 145 | init() -> |
Harald Welte | 3b10578 | 2012-03-29 21:36:15 +0200 | [diff] [blame] | 146 | ets:new(sccp_masq_orig, [ordered_set, public, named_table, |
| 147 | {keypos, #sccp_masq_rec.digits_in}]), |
| 148 | ets:new(sccp_masq_rev, [ordered_set, public, named_table, |
| 149 | {keypos, #sccp_masq_rec.digits_in}]), |
Harald Welte | 20ad1ba | 2011-02-07 20:50:39 +0100 | [diff] [blame] | 150 | ok. |
| 151 | |
| 152 | reset() -> |
| 153 | io:format("SCCP MASQ: Deleting all MASQ state records~n"), |
Harald Welte | 3b10578 | 2012-03-29 21:36:15 +0200 | [diff] [blame] | 154 | ets:delete_all_objects(sccp_masq_orig), |
| 155 | ets:delete_all_objects(sccp_masq_rev). |
Harald Welte | af3a9fc | 2011-02-25 10:36:43 +0100 | [diff] [blame] | 156 | |
| 157 | dump() -> |
Harald Welte | 3b10578 | 2012-03-29 21:36:15 +0200 | [diff] [blame] | 158 | ListOrig = ets:tab2list(sccp_masq_orig), |
| 159 | ListRev = ets:tab2list(sccp_masq_rev), |
Harald Welte | af3a9fc | 2011-02-25 10:36:43 +0100 | [diff] [blame] | 160 | io:format("SCCP MASQ Table Dump (ORIGINAL)~n"), |
| 161 | dump_list(ListOrig), |
| 162 | io:format("SCCP MASQ Table Dump (REVERSE)~n"), |
| 163 | dump_list(ListRev). |
| 164 | |
| 165 | dump_list([]) -> |
| 166 | ok; |
| 167 | dump_list([#sccp_masq_rec{digits_in=DigIn, digits_out=DigOut}|Tail]) -> |
| 168 | DigInInt = osmo_util:digit_list2int(DigIn), |
| 169 | DigOutInt = osmo_util:digit_list2int(DigOut), |
| 170 | io:format("~p -> ~p~n", [DigInInt, DigOutInt]), |
| 171 | dump_list(Tail). |