Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 1 | /* NS Provider for NS/UDP/IP |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 2 | * (C) 2020-2021 Harald Welte <laforge@gnumonks.org> |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 3 | * contributions by sysmocom - s.f.m.c. GmbH |
| 4 | * All rights reserved. |
| 5 | * |
| 6 | * Released under the terms of GNU General Public License, Version 2 or |
| 7 | * (at your option) any later version. |
| 8 | * |
| 9 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 10 | */ |
| 11 | |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 12 | /* This provider can be operated in two modes: |
| 13 | * |
| 14 | * 1) the "classic" mode, where - similar to the NS_Provider_FR - there is |
| 15 | * only one NSVC per provider. In this mode, the "NSE" port is used to |
| 16 | * exchange data with the next higher level component, such as a NSVC_CT |
| 17 | * or a RAW_NS_CT. |
| 18 | * |
Alexander Couzens | 99b837a | 2021-05-25 01:14:45 +0200 | [diff] [blame] | 19 | * 2) the new "endpoint" mode, where one provider can host a number of different |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 20 | * NSVCs. This is needed in most non-trivial IP-SNS scenarios. The 'NSE' |
| 21 | * port of this component is no longer used. Instead, there is a NSVC port |
Alexander Couzens | 99b837a | 2021-05-25 01:14:45 +0200 | [diff] [blame] | 22 | * array, one of which will be used for each NSVC. The NSVCs are dynamically |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 23 | * added and removed via the PROC procedure port, controlled by NS_CT. |
| 24 | */ |
| 25 | |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 26 | module NS_Provider_IPL4 { |
| 27 | |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 28 | import from Misc_Helpers all; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 29 | import from NS_Emulation all; |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 30 | import from RAW_NS all; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 31 | import from NS_Types all; |
| 32 | |
| 33 | import from IPL4asp_Types all; |
| 34 | import from IPL4asp_PortType all; |
| 35 | |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 36 | /* maximum number of NS-VCs within one Provider (== IP endpoint) */ |
| 37 | private const integer NUM_MAX_NSVC := 16; |
| 38 | |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 39 | type component NS_Provider_IPL4_CT extends NS_Provider_CT { |
| 40 | /* down-facing port towards IPL4asp to IUT */ |
| 41 | port IPL4asp_PT IPL4; |
| 42 | var integer g_conn_id := -1; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 43 | |
| 44 | /* per-NSVC ports and state */ |
| 45 | port NS_PROVIDER_PT NSVC[NUM_MAX_NSVC]; |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 46 | var boolean g_nsvc_bound[NUM_MAX_NSVC]; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 47 | var PerNsvcState g_nsvc[NUM_MAX_NSVC]; |
| 48 | |
| 49 | /* management port via which */ |
| 50 | port NSPIP_PROC_PT PROC; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 51 | }; |
| 52 | |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 53 | type record PerNsvcState { |
| 54 | charstring remote_ip, |
| 55 | PortNumber remote_port, |
| 56 | NSVC_CT vc_nsvc |
| 57 | }; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 58 | |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 59 | signature NSPIP_add_nsvc(charstring remote_ip, PortNumber remote_port, NSVC_CT vc_nsvc) return integer; |
| 60 | signature NSPIP_del_nsvc(charstring remote_ip, PortNumber remote_port) return integer; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 61 | |
| 62 | type port NSPIP_PROC_PT procedure { |
| 63 | inout NSPIP_add_nsvc, NSPIP_del_nsvc; |
| 64 | } with { extension "internal" }; |
| 65 | |
| 66 | /* add a new NSVC to the provider */ |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 67 | private function f_nsvc_add(PerNsvcState nsvc) runs on NS_Provider_IPL4_CT return integer |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 68 | { |
| 69 | for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 70 | if (g_nsvc_bound[i] == false) { |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 71 | g_nsvc[i] := nsvc; |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 72 | g_nsvc_bound[i] := true; |
| 73 | if (isbound(nsvc.vc_nsvc) and nsvc.vc_nsvc != null) { |
| 74 | connect(self:NSVC[i], nsvc.vc_nsvc:NSCP); |
| 75 | NSVC[i].send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_UP}); |
| 76 | } |
| 77 | return i; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 78 | } |
| 79 | } |
| 80 | Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Overflow of g_nsvc array")); |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 81 | return -1; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 82 | } |
| 83 | |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 84 | private function f_nsvc_del(PerNsvcState nsvc) runs on NS_Provider_IPL4_CT return integer |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 85 | { |
| 86 | for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 87 | if (g_nsvc_bound[i] and |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 88 | g_nsvc[i].remote_ip == nsvc.remote_ip and |
| 89 | g_nsvc[i].remote_port == nsvc.remote_port) { |
| 90 | g_nsvc[i] := { |
| 91 | remote_ip := -, |
| 92 | remote_port := -, |
| 93 | vc_nsvc := null |
| 94 | } |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 95 | g_nsvc_bound[i] := false; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 96 | NSVC[i].send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_DOWN}); |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 97 | if (isbound(g_nsvc[i].vc_nsvc) and g_nsvc[i].vc_nsvc != null) { |
| 98 | disconnect(self:NSVC[i], nsvc.vc_nsvc:NSCP); |
| 99 | } |
| 100 | return i; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 101 | } |
| 102 | } |
| 103 | Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("attempt to delete unknown NSVC")); |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 104 | return -1; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | private function f_get_nsvc_idx(charstring remote_ip, PortNumber remote_port) |
| 108 | runs on NS_Provider_IPL4_CT return integer |
| 109 | { |
| 110 | for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 111 | if (g_nsvc_bound[i] and |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 112 | g_nsvc[i].remote_ip == remote_ip and g_nsvc[i].remote_port == remote_port) { |
| 113 | return i; |
| 114 | } |
| 115 | } |
| 116 | return -1; |
| 117 | } |
| 118 | |
| 119 | function main(NSVCConfiguration config, NSConfiguration nsconfig, charstring id) runs on NS_Provider_IPL4_CT { |
| 120 | for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
| 121 | g_nsvc[i].vc_nsvc := null; |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 122 | g_nsvc_bound[i] := false; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 123 | } |
| 124 | |
| 125 | /* in order to support any number of NSVC on this endpoiint, we only bind the socket |
| 126 | * to its local ip/port, but do not connect it to the remote peer provided in 'config'. */ |
Alexander Couzens | 3ee8268 | 2020-09-17 23:53:32 +0200 | [diff] [blame] | 127 | map(self:IPL4, system:IPL4); |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 128 | var Result res := f_IPL4_listen(IPL4, config.provider.ip.local_ip, |
| 129 | config.provider.ip.local_udp_port, { udp := {}}); |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 130 | if (not ispresent(res.connId)) { |
Harald Welte | 5e8573e | 2020-09-13 15:32:56 +0200 | [diff] [blame] | 131 | setverdict(fail, "Could not connect NS UDP socket ", config.provider.ip); |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 132 | mtc.stop; |
| 133 | } |
| 134 | g_conn_id := res.connId; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 135 | |
| 136 | if (NSE.checkstate("Connected")) { |
| 137 | NSE.send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_UP}); |
| 138 | } |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 139 | |
Alexander Couzens | 6da7aaf | 2021-01-10 21:40:43 +0100 | [diff] [blame] | 140 | /* transceive between user-facing port and UDP socket */ |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 141 | while (true) { |
| 142 | var ASP_RecvFrom rx_rf; |
| 143 | var PDU_NS rx_pdu; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 144 | var integer rx_idx; |
| 145 | var charstring remote_ip; |
| 146 | var PortNumber remote_port; |
| 147 | var NSVC_CT vc_nsvc; |
| 148 | var NS_CT vc_caller; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 149 | alt { |
| 150 | |
| 151 | [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf { |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 152 | /* we have to resolve the NS-VC based on the remote peer */ |
| 153 | var integer nsvc_idx := f_get_nsvc_idx(rx_rf.remName, rx_rf.remPort); |
| 154 | if (nsvc_idx == -1) { |
| 155 | /* backwards compatibility; if there's no NSVC, send to NSE port */ |
| 156 | NSE.send(dec_PDU_NS(rx_rf.msg)); |
| 157 | } else { |
| 158 | /* endpoint mode; send to the per-NSVC component via NSVC port */ |
| 159 | NSVC[nsvc_idx].send(dec_PDU_NS(rx_rf.msg)); |
| 160 | } |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | [] IPL4.receive(ASP_ConnId_ReadyToRelease:?) { |
| 164 | } |
| 165 | |
| 166 | [] IPL4.receive(ASP_Event:?) { |
| 167 | } |
| 168 | |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 169 | [] any from NSVC.receive(PDU_NS:?) -> value rx_pdu @index value rx_idx { |
| 170 | /* we can use the port array index directly into the g_nsvc array in order |
| 171 | * to resolve the IP + port of the remote peer to which to send */ |
| 172 | var ASP_SendTo tx := { |
| 173 | connId := g_conn_id, |
| 174 | remName := g_nsvc[rx_idx].remote_ip, |
| 175 | remPort := g_nsvc[rx_idx].remote_port, |
| 176 | proto := { udp := {} }, |
| 177 | msg := enc_PDU_NS(rx_pdu) |
| 178 | }; |
| 179 | IPL4.send(tx); |
| 180 | } |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 181 | [] NSE.receive(PDU_NS:?) -> value rx_pdu { |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 182 | /* backwards compatibility: If user uses the NSE port, use the destination |
| 183 | * provided during main() initialization */ |
| 184 | var ASP_SendTo tx := { |
| 185 | connId := g_conn_id, |
| 186 | remName := config.provider.ip.remote_ip, |
| 187 | remPort := config.provider.ip.remote_udp_port, |
| 188 | proto := { udp := {} }, |
| 189 | msg := enc_PDU_NS(rx_pdu) |
| 190 | }; |
| 191 | IPL4.send(tx); |
| 192 | } |
| 193 | |
| 194 | /* procedure port to add/remove NSVCs from this provider */ |
| 195 | [] PROC.getcall(NSPIP_add_nsvc:{?,?,?}) -> param (remote_ip, remote_port, vc_nsvc) sender vc_caller { |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 196 | var integer idx; |
| 197 | idx := f_nsvc_add(PerNsvcState:{remote_ip, remote_port, vc_nsvc}); |
| 198 | PROC.reply(NSPIP_add_nsvc:{remote_ip, remote_port, vc_nsvc} value idx) to vc_caller; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 199 | } |
| 200 | [] PROC.getcall(NSPIP_del_nsvc:{?,?}) -> param (remote_ip, remote_port) sender vc_caller { |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 201 | var integer idx; |
| 202 | idx := f_nsvc_del(PerNsvcState:{remote_ip, remote_port}); |
| 203 | PROC.reply(NSPIP_del_nsvc:{remote_ip, remote_port} value idx) to vc_caller; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | } /* alt */ |
| 207 | } /* while */ |
| 208 | |
| 209 | } /* main */ |
| 210 | |
Harald Welte | ad32071 | 2021-03-29 21:56:48 +0200 | [diff] [blame] | 211 | function f_nspip_add_nsvc(NS_Provider_IPL4_CT vc_ipep, charstring remote_ip, PortNumber remote_port, NSVC_CT vc_nsvc) |
| 212 | runs on NS_CT { |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 213 | var integer idx := -1; |
Harald Welte | ad32071 | 2021-03-29 21:56:48 +0200 | [diff] [blame] | 214 | NSPIP_PROC.call(NSPIP_add_nsvc:{remote_ip, remote_port, vc_nsvc}) to vc_ipep { |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 215 | [] NSPIP_PROC.getreply(NSPIP_add_nsvc:{?,?,?}) -> value idx; |
Harald Welte | ad32071 | 2021-03-29 21:56:48 +0200 | [diff] [blame] | 216 | } |
| 217 | } |
| 218 | |
Alexander Couzens | bd6e9a1 | 2021-06-07 00:45:15 +0200 | [diff] [blame] | 219 | function f_nspip_add_nsvc2(NS_Provider_IPL4_CT vc_ipep, charstring remote_ip, PortNumber remote_port) |
| 220 | runs on RAW_NS_CT return integer { |
| 221 | var integer idx := -1; |
| 222 | NSPIP_PROC.call(NSPIP_add_nsvc:{remote_ip, remote_port, null}) to vc_ipep { |
| 223 | [] NSPIP_PROC.getreply(NSPIP_add_nsvc:{?,?,?}) -> value idx; |
| 224 | } |
| 225 | |
| 226 | return idx; |
| 227 | } |
| 228 | |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 229 | } /* module */ |