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 | * |
| 19 | * 2) the enew "endpoint" mode, where one provider can host a number of different |
| 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 |
| 22 | * array, one of which will be used for each NSVC. The NSVCs are dynamically |
| 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; |
| 30 | import from NS_Types all; |
| 31 | |
| 32 | import from IPL4asp_Types all; |
| 33 | import from IPL4asp_PortType all; |
| 34 | |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 35 | /* maximum number of NS-VCs within one Provider (== IP endpoint) */ |
| 36 | private const integer NUM_MAX_NSVC := 16; |
| 37 | |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 38 | type component NS_Provider_IPL4_CT extends NS_Provider_CT { |
| 39 | /* down-facing port towards IPL4asp to IUT */ |
| 40 | port IPL4asp_PT IPL4; |
| 41 | var integer g_conn_id := -1; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 42 | |
| 43 | /* per-NSVC ports and state */ |
| 44 | port NS_PROVIDER_PT NSVC[NUM_MAX_NSVC]; |
| 45 | var PerNsvcState g_nsvc[NUM_MAX_NSVC]; |
| 46 | |
| 47 | /* management port via which */ |
| 48 | port NSPIP_PROC_PT PROC; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 49 | }; |
| 50 | |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 51 | type record PerNsvcState { |
| 52 | charstring remote_ip, |
| 53 | PortNumber remote_port, |
| 54 | NSVC_CT vc_nsvc |
| 55 | }; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 56 | |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 57 | signature NSPIP_add_nsvc(charstring remote_ip, PortNumber remote_port, NSVC_CT vc_nsvc); |
| 58 | signature NSPIP_del_nsvc(charstring remote_ip, PortNumber remote_port); |
| 59 | |
| 60 | type port NSPIP_PROC_PT procedure { |
| 61 | inout NSPIP_add_nsvc, NSPIP_del_nsvc; |
| 62 | } with { extension "internal" }; |
| 63 | |
| 64 | /* add a new NSVC to the provider */ |
| 65 | private function f_nsvc_add(PerNsvcState nsvc) runs on NS_Provider_IPL4_CT |
| 66 | { |
| 67 | for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
| 68 | if (g_nsvc[i].vc_nsvc == null) { |
| 69 | g_nsvc[i] := nsvc; |
| 70 | connect(self:NSVC[i], nsvc.vc_nsvc:NSCP); |
| 71 | NSVC[i].send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_UP}); |
| 72 | return; |
| 73 | } |
| 74 | } |
| 75 | Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Overflow of g_nsvc array")); |
| 76 | } |
| 77 | |
| 78 | private function f_nsvc_del(PerNsvcState nsvc) runs on NS_Provider_IPL4_CT |
| 79 | { |
| 80 | for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
| 81 | if (g_nsvc[i].vc_nsvc != null and |
| 82 | g_nsvc[i].remote_ip == nsvc.remote_ip and |
| 83 | g_nsvc[i].remote_port == nsvc.remote_port) { |
| 84 | g_nsvc[i] := { |
| 85 | remote_ip := -, |
| 86 | remote_port := -, |
| 87 | vc_nsvc := null |
| 88 | } |
| 89 | NSVC[i].send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_DOWN}); |
| 90 | disconnect(self:NSVC[i], nsvc.vc_nsvc:NSCP); |
| 91 | return; |
| 92 | } |
| 93 | } |
| 94 | Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("attempt to delete unknown NSVC")); |
| 95 | } |
| 96 | |
| 97 | private function f_get_nsvc_idx(charstring remote_ip, PortNumber remote_port) |
| 98 | runs on NS_Provider_IPL4_CT return integer |
| 99 | { |
| 100 | for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
| 101 | if (g_nsvc[i].vc_nsvc != null and |
| 102 | g_nsvc[i].remote_ip == remote_ip and g_nsvc[i].remote_port == remote_port) { |
| 103 | return i; |
| 104 | } |
| 105 | } |
| 106 | return -1; |
| 107 | } |
| 108 | |
| 109 | function main(NSVCConfiguration config, NSConfiguration nsconfig, charstring id) runs on NS_Provider_IPL4_CT { |
| 110 | for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
| 111 | g_nsvc[i].vc_nsvc := null; |
| 112 | } |
| 113 | |
| 114 | /* in order to support any number of NSVC on this endpoiint, we only bind the socket |
| 115 | * 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] | 116 | map(self:IPL4, system:IPL4); |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 117 | var Result res := f_IPL4_listen(IPL4, config.provider.ip.local_ip, |
| 118 | config.provider.ip.local_udp_port, { udp := {}}); |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 119 | if (not ispresent(res.connId)) { |
Harald Welte | 5e8573e | 2020-09-13 15:32:56 +0200 | [diff] [blame] | 120 | setverdict(fail, "Could not connect NS UDP socket ", config.provider.ip); |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 121 | mtc.stop; |
| 122 | } |
| 123 | g_conn_id := res.connId; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 124 | |
| 125 | if (NSE.checkstate("Connected")) { |
| 126 | NSE.send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_UP}); |
| 127 | } |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 128 | |
Alexander Couzens | 6da7aaf | 2021-01-10 21:40:43 +0100 | [diff] [blame] | 129 | /* transceive between user-facing port and UDP socket */ |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 130 | while (true) { |
| 131 | var ASP_RecvFrom rx_rf; |
| 132 | var PDU_NS rx_pdu; |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 133 | var integer rx_idx; |
| 134 | var charstring remote_ip; |
| 135 | var PortNumber remote_port; |
| 136 | var NSVC_CT vc_nsvc; |
| 137 | var NS_CT vc_caller; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 138 | alt { |
| 139 | |
| 140 | [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf { |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 141 | /* we have to resolve the NS-VC based on the remote peer */ |
| 142 | var integer nsvc_idx := f_get_nsvc_idx(rx_rf.remName, rx_rf.remPort); |
| 143 | if (nsvc_idx == -1) { |
| 144 | /* backwards compatibility; if there's no NSVC, send to NSE port */ |
| 145 | NSE.send(dec_PDU_NS(rx_rf.msg)); |
| 146 | } else { |
| 147 | /* endpoint mode; send to the per-NSVC component via NSVC port */ |
| 148 | NSVC[nsvc_idx].send(dec_PDU_NS(rx_rf.msg)); |
| 149 | } |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | [] IPL4.receive(ASP_ConnId_ReadyToRelease:?) { |
| 153 | } |
| 154 | |
| 155 | [] IPL4.receive(ASP_Event:?) { |
| 156 | } |
| 157 | |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 158 | [] any from NSVC.receive(PDU_NS:?) -> value rx_pdu @index value rx_idx { |
| 159 | /* we can use the port array index directly into the g_nsvc array in order |
| 160 | * to resolve the IP + port of the remote peer to which to send */ |
| 161 | var ASP_SendTo tx := { |
| 162 | connId := g_conn_id, |
| 163 | remName := g_nsvc[rx_idx].remote_ip, |
| 164 | remPort := g_nsvc[rx_idx].remote_port, |
| 165 | proto := { udp := {} }, |
| 166 | msg := enc_PDU_NS(rx_pdu) |
| 167 | }; |
| 168 | IPL4.send(tx); |
| 169 | } |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 170 | [] NSE.receive(PDU_NS:?) -> value rx_pdu { |
Harald Welte | 4bef0dd | 2021-03-29 21:45:46 +0200 | [diff] [blame] | 171 | /* backwards compatibility: If user uses the NSE port, use the destination |
| 172 | * provided during main() initialization */ |
| 173 | var ASP_SendTo tx := { |
| 174 | connId := g_conn_id, |
| 175 | remName := config.provider.ip.remote_ip, |
| 176 | remPort := config.provider.ip.remote_udp_port, |
| 177 | proto := { udp := {} }, |
| 178 | msg := enc_PDU_NS(rx_pdu) |
| 179 | }; |
| 180 | IPL4.send(tx); |
| 181 | } |
| 182 | |
| 183 | /* procedure port to add/remove NSVCs from this provider */ |
| 184 | [] PROC.getcall(NSPIP_add_nsvc:{?,?,?}) -> param (remote_ip, remote_port, vc_nsvc) sender vc_caller { |
| 185 | f_nsvc_add(PerNsvcState:{remote_ip, remote_port, vc_nsvc}); |
| 186 | PROC.reply(NSPIP_add_nsvc:{remote_ip, remote_port, vc_nsvc}) to vc_caller; |
| 187 | } |
| 188 | [] PROC.getcall(NSPIP_del_nsvc:{?,?}) -> param (remote_ip, remote_port) sender vc_caller { |
| 189 | f_nsvc_del(PerNsvcState:{remote_ip, remote_port}); |
| 190 | PROC.reply(NSPIP_del_nsvc:{remote_ip, remote_port}) to vc_caller; |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 191 | } |
| 192 | |
| 193 | } /* alt */ |
| 194 | } /* while */ |
| 195 | |
| 196 | } /* main */ |
| 197 | |
Harald Welte | ad32071 | 2021-03-29 21:56:48 +0200 | [diff] [blame] | 198 | function f_nspip_add_nsvc(NS_Provider_IPL4_CT vc_ipep, charstring remote_ip, PortNumber remote_port, NSVC_CT vc_nsvc) |
| 199 | runs on NS_CT { |
| 200 | NSPIP_PROC.call(NSPIP_add_nsvc:{remote_ip, remote_port, vc_nsvc}) to vc_ipep { |
| 201 | [] NSPIP_PROC.getreply(NSPIP_add_nsvc:{?,?,?}); |
| 202 | } |
| 203 | } |
| 204 | |
Harald Welte | 013d65a | 2020-09-13 14:41:31 +0200 | [diff] [blame] | 205 | } /* module */ |