| /* NS Provider for NS/UDP/IP |
| * (C) 2020-2021 Harald Welte <laforge@gnumonks.org> |
| * contributions by sysmocom - s.f.m.c. GmbH |
| * All rights reserved. |
| * |
| * Released under the terms of GNU General Public License, Version 2 or |
| * (at your option) any later version. |
| * |
| * SPDX-License-Identifier: GPL-2.0-or-later |
| */ |
| |
| /* This provider can be operated in two modes: |
| * |
| * 1) the "classic" mode, where - similar to the NS_Provider_FR - there is |
| * only one NSVC per provider. In this mode, the "NSE" port is used to |
| * exchange data with the next higher level component, such as a NSVC_CT |
| * or a RAW_NS_CT. |
| * |
| * 2) the new "endpoint" mode, where one provider can host a number of different |
| * NSVCs. This is needed in most non-trivial IP-SNS scenarios. The 'NSE' |
| * port of this component is no longer used. Instead, there is a NSVC port |
| * array, one of which will be used for each NSVC. The NSVCs are dynamically |
| * added and removed via the PROC procedure port, controlled by NS_CT. |
| */ |
| |
| module NS_Provider_IPL4 { |
| |
| import from Misc_Helpers all; |
| import from NS_Emulation all; |
| import from RAW_NS all; |
| import from NS_Types all; |
| |
| import from IPL4asp_Types all; |
| import from IPL4asp_PortType all; |
| |
| /* maximum number of NS-VCs within one Provider (== IP endpoint) */ |
| private const integer NUM_MAX_NSVC := 16; |
| |
| type component NS_Provider_IPL4_CT extends NS_Provider_CT { |
| /* down-facing port towards IPL4asp to IUT */ |
| port IPL4asp_PT IPL4; |
| var integer g_conn_id := -1; |
| |
| /* per-NSVC ports and state */ |
| port NS_PROVIDER_PT NSVC[NUM_MAX_NSVC]; |
| var boolean g_nsvc_bound[NUM_MAX_NSVC]; |
| var PerNsvcState g_nsvc[NUM_MAX_NSVC]; |
| |
| /* management port via which */ |
| port NSPIP_PROC_PT PROC; |
| }; |
| |
| type record PerNsvcState { |
| charstring remote_ip, |
| PortNumber remote_port, |
| NSVC_CT vc_nsvc |
| }; |
| |
| signature NSPIP_add_nsvc(charstring remote_ip, PortNumber remote_port, NSVC_CT vc_nsvc) return integer; |
| signature NSPIP_del_nsvc(charstring remote_ip, PortNumber remote_port) return integer; |
| |
| type port NSPIP_PROC_PT procedure { |
| inout NSPIP_add_nsvc, NSPIP_del_nsvc; |
| } with { extension "internal" }; |
| |
| /* add a new NSVC to the provider */ |
| private function f_nsvc_add(PerNsvcState nsvc) runs on NS_Provider_IPL4_CT return integer |
| { |
| for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
| if (g_nsvc_bound[i] == false) { |
| g_nsvc[i] := nsvc; |
| g_nsvc_bound[i] := true; |
| if (isbound(nsvc.vc_nsvc) and nsvc.vc_nsvc != null) { |
| connect(self:NSVC[i], nsvc.vc_nsvc:NSCP); |
| NSVC[i].send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_UP}); |
| } |
| return i; |
| } |
| } |
| Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Overflow of g_nsvc array")); |
| return -1; |
| } |
| |
| private function f_nsvc_del(PerNsvcState nsvc) runs on NS_Provider_IPL4_CT return integer |
| { |
| for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
| if (g_nsvc_bound[i] and |
| g_nsvc[i].remote_ip == nsvc.remote_ip and |
| g_nsvc[i].remote_port == nsvc.remote_port) { |
| g_nsvc[i] := { |
| remote_ip := -, |
| remote_port := -, |
| vc_nsvc := null |
| } |
| g_nsvc_bound[i] := false; |
| NSVC[i].send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_DOWN}); |
| if (isbound(g_nsvc[i].vc_nsvc) and g_nsvc[i].vc_nsvc != null) { |
| disconnect(self:NSVC[i], nsvc.vc_nsvc:NSCP); |
| } |
| return i; |
| } |
| } |
| Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("attempt to delete unknown NSVC")); |
| return -1; |
| } |
| |
| private function f_get_nsvc_idx(charstring remote_ip, PortNumber remote_port) |
| runs on NS_Provider_IPL4_CT return integer |
| { |
| for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
| if (g_nsvc_bound[i] and |
| g_nsvc[i].remote_ip == remote_ip and g_nsvc[i].remote_port == remote_port) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| function main(NSVCConfiguration config, NSConfiguration nsconfig, charstring id) runs on NS_Provider_IPL4_CT { |
| for (var integer i := 0; i < sizeof(g_nsvc); i := i+1) { |
| g_nsvc[i].vc_nsvc := null; |
| g_nsvc_bound[i] := false; |
| } |
| |
| /* in order to support any number of NSVC on this endpoiint, we only bind the socket |
| * to its local ip/port, but do not connect it to the remote peer provided in 'config'. */ |
| map(self:IPL4, system:IPL4); |
| var Result res := f_IPL4_listen(IPL4, config.provider.ip.local_ip, |
| config.provider.ip.local_udp_port, { udp := {}}); |
| if (not ispresent(res.connId)) { |
| setverdict(fail, "Could not connect NS UDP socket ", config.provider.ip); |
| mtc.stop; |
| } |
| g_conn_id := res.connId; |
| |
| if (NSE.checkstate("Connected")) { |
| NSE.send(NS_Provider_Evt:{link_status := NS_PROV_LINK_STATUS_UP}); |
| } |
| |
| /* transceive between user-facing port and UDP socket */ |
| while (true) { |
| var ASP_RecvFrom rx_rf; |
| var PDU_NS rx_pdu; |
| var integer rx_idx; |
| var charstring remote_ip; |
| var PortNumber remote_port; |
| var NSVC_CT vc_nsvc; |
| var NS_CT vc_caller; |
| alt { |
| |
| [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf { |
| /* we have to resolve the NS-VC based on the remote peer */ |
| var integer nsvc_idx := f_get_nsvc_idx(rx_rf.remName, rx_rf.remPort); |
| if (nsvc_idx == -1) { |
| /* backwards compatibility; if there's no NSVC, send to NSE port */ |
| NSE.send(dec_PDU_NS(rx_rf.msg)); |
| } else { |
| /* endpoint mode; send to the per-NSVC component via NSVC port */ |
| NSVC[nsvc_idx].send(dec_PDU_NS(rx_rf.msg)); |
| } |
| } |
| |
| [] IPL4.receive(ASP_ConnId_ReadyToRelease:?) { |
| } |
| |
| [] IPL4.receive(ASP_Event:?) { |
| } |
| |
| [] any from NSVC.receive(PDU_NS:?) -> value rx_pdu @index value rx_idx { |
| /* we can use the port array index directly into the g_nsvc array in order |
| * to resolve the IP + port of the remote peer to which to send */ |
| var ASP_SendTo tx := { |
| connId := g_conn_id, |
| remName := g_nsvc[rx_idx].remote_ip, |
| remPort := g_nsvc[rx_idx].remote_port, |
| proto := { udp := {} }, |
| msg := enc_PDU_NS(rx_pdu) |
| }; |
| IPL4.send(tx); |
| } |
| [] NSE.receive(PDU_NS:?) -> value rx_pdu { |
| /* backwards compatibility: If user uses the NSE port, use the destination |
| * provided during main() initialization */ |
| var ASP_SendTo tx := { |
| connId := g_conn_id, |
| remName := config.provider.ip.remote_ip, |
| remPort := config.provider.ip.remote_udp_port, |
| proto := { udp := {} }, |
| msg := enc_PDU_NS(rx_pdu) |
| }; |
| IPL4.send(tx); |
| } |
| |
| /* procedure port to add/remove NSVCs from this provider */ |
| [] PROC.getcall(NSPIP_add_nsvc:{?,?,?}) -> param (remote_ip, remote_port, vc_nsvc) sender vc_caller { |
| var integer idx; |
| idx := f_nsvc_add(PerNsvcState:{remote_ip, remote_port, vc_nsvc}); |
| PROC.reply(NSPIP_add_nsvc:{remote_ip, remote_port, vc_nsvc} value idx) to vc_caller; |
| } |
| [] PROC.getcall(NSPIP_del_nsvc:{?,?}) -> param (remote_ip, remote_port) sender vc_caller { |
| var integer idx; |
| idx := f_nsvc_del(PerNsvcState:{remote_ip, remote_port}); |
| PROC.reply(NSPIP_del_nsvc:{remote_ip, remote_port} value idx) to vc_caller; |
| } |
| |
| } /* alt */ |
| } /* while */ |
| |
| } /* main */ |
| |
| function f_nspip_add_nsvc(NS_Provider_IPL4_CT vc_ipep, charstring remote_ip, PortNumber remote_port, NSVC_CT vc_nsvc) |
| runs on NS_CT { |
| var integer idx := -1; |
| NSPIP_PROC.call(NSPIP_add_nsvc:{remote_ip, remote_port, vc_nsvc}) to vc_ipep { |
| [] NSPIP_PROC.getreply(NSPIP_add_nsvc:{?,?,?}) -> value idx; |
| } |
| } |
| |
| function f_nspip_add_nsvc2(NS_Provider_IPL4_CT vc_ipep, charstring remote_ip, PortNumber remote_port) |
| runs on RAW_NS_CT return integer { |
| var integer idx := -1; |
| NSPIP_PROC.call(NSPIP_add_nsvc:{remote_ip, remote_port, null}) to vc_ipep { |
| [] NSPIP_PROC.getreply(NSPIP_add_nsvc:{?,?,?}) -> value idx; |
| } |
| |
| return idx; |
| } |
| |
| } /* module */ |