pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/40764?usp=email )
Change subject: 5gc: Introduce test TC_register_ping4 ...................................................................... 5gc: Introduce test TC_register_ping4 osmo-uecups is used to set up a GTPv1U tundev with the PDU session params configured over NGAP/NAS, similar to what is already done in PGW_Tests. Depends: osmo-uecups.git Change-Id I6262c3dfbf774b361aadf0aa53ce09b5fdc38da4 Change-Id: I66737a94eedcf4184aa59bbbd7cc700d1e9d1521 --- M 5gc/C5G_Tests.cfg M 5gc/C5G_Tests.ttcn M 5gc/ConnHdlr.ttcn M 5gc/expected-results.xml M 5gc/gen_links.sh M 5gc/open5gs/open5gs-smf.yaml M 5gc/open5gs/open5gs-upf.yaml A 5gc/osmo-uecups-daemon.cfg M 5gc/regen_makefile.sh M 5gc/testenv.cfg R 5gc/testenv.sh A 5gc/testsuite_prepare.sh M deps/Makefile 13 files changed, 226 insertions(+), 43 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/64/40764/1 diff --git a/5gc/C5G_Tests.cfg b/5gc/C5G_Tests.cfg index ec6f129..9d4439c 100644 --- a/5gc/C5G_Tests.cfg +++ b/5gc/C5G_Tests.cfg @@ -19,6 +19,10 @@ C5G_Tests.mp_imsi := '999700000000000'H; C5G_Tests.mp_usim_key := '762a2206fe0b4151ace403c86a11e479'O; C5G_Tests.mp_usim_opc := '3c6e0b8a9c15224a8228b9a98ca1531d'O; +C5G_Tests.mp_run_prog_log_path := "./run_prog" +C5G_Tests.mp_run_prog_as_user := "osmocom" # testenv.sh sets this to $USER +C5G_Tests.mp_ping_hostname := "10.45.0.1"; +GTPv1U_Emulation.mp_uecups_host := "127.0.0.20"; [MAIN_CONTROLLER] diff --git a/5gc/C5G_Tests.ttcn b/5gc/C5G_Tests.ttcn index f09b364..ff6e43f 100644 --- a/5gc/C5G_Tests.ttcn +++ b/5gc/C5G_Tests.ttcn @@ -45,6 +45,8 @@ import from NG_NAS_Functions all; import from NG_CryptoFunctions all; +import from GTPv1U_Emulation all; + import from ConnHdlr all; /* (maximum) number of emulated UEs */ @@ -63,7 +65,20 @@ octetstring mp_usim_opc := '3c6e0b8a9c15224a8228b9a98ca1531d'O; uint24_t mp_tac := 1; charstring mp_apn := "internet"; - charstring mp_local_gtpu_ip := "127.0.0.1"; + charstring mp_local_gtpu_ip := "127.0.0.20"; + charstring mp_ping_hostname := "10.45.0.1"; + + charstring mp_run_prog_log_path := "/tmp"; + charstring mp_run_prog_as_user := "osmocom"; +} + +template (value) RunProgParams ts_RunProgParams(integer imsi_suffix) := +{ + run_as_user := mp_run_prog_as_user, + log_path_prefix := mp_run_prog_log_path, + tun_netns_name := "tun" & int2str(imsi_suffix), + tun_dev_name := "tun" & int2str(imsi_suffix), + ping_hostname := mp_ping_hostname } template (value) UeParams ts_UeParams(integer imsi_suffix) := @@ -73,14 +88,13 @@ usim_key := mp_usim_key, usim_opc := mp_usim_opc, apn := mp_apn, + run_prog_pars := ts_RunProgParams(imsi_suffix), ran_gtpu_ip := mp_local_gtpu_ip, - ran_gtpu_teid := int2oct(imsi_suffix, 4), + ran_gtpu_teid := int2oct(imsi_suffix + 1, 4), cn_gtpu_ip := omit, cn_gtpu_teid := omit, - ue_ip := omit, - kausf := omit, - kseaf := omit, - kamf := omit + qfi := omit, + ue_ip := omit } type component MTC_CT { @@ -90,6 +104,9 @@ port NGAP_PT NGAP_UNIT[NUM_NGRAN]; port NGAPEM_PROC_PT NGAP_PROC[NUM_NGRAN]; + var GTPv1U_Emulation_CT vc_GTP1U; + port GTP1UEM_PT TEID0; + timer g_Tguard := 30.0; var UeParams g_ue_pars[NUM_UE]; @@ -154,11 +171,25 @@ } } +private function f_init_gtp1u() runs on MTC_CT { + var Gtp1uEmulationCfg cfg := { + gtpu_bind_ip := omit, /* using gtpu daemon */ + gtpu_bind_port := omit, /* using gtpu daemon */ + use_gtpu_daemon := true + }; + + vc_GTP1U := GTPv1U_Emulation_CT.create("GTP1U_EM"); + map(vc_GTP1U:GTP1U, system:GTP1U); + connect(vc_GTP1U:TEID0, self:TEID0); + vc_GTP1U.start(GTPv1U_Emulation.main(cfg)); +} + private function f_init(integer imsi_suffix := 0, float t_guard := 30.0) runs on MTC_CT { /* start guard timer and activate it as default */ g_Tguard.start(t_guard); activate(as_Tguard()); + f_init_gtp1u(); f_init_ngap(imsi_suffix); } @@ -185,6 +216,10 @@ connect(vc_conn:NGAP, vc_NGAP[ngap_idx]:NGAP_CLIENT); connect(vc_conn:NGAP_PROC, vc_NGAP[ngap_idx]:NGAP_PROC); + /* GTPv1U */ + connect(vc_conn:GTP1U[0], vc_GTP1U:CLIENT); + connect(vc_conn:GTP1U_PROC[0], vc_GTP1U:CLIENT_PROC); + /* We cannot use vc_conn.start(f_init_handler(fn, id, pars)); as we cannot have * a stand-alone 'derefers()' call, see https://www.eclipse.org/forums/index.php/t/1091364/ */ vc_conn.start(f_init_handler(fn, pars)); @@ -278,11 +313,29 @@ vc_conn.done; } +private function f_TC_register_ping4() runs on ConnHdlr { + f_register(); + f_pdu_sess_create_tun(); + f_sleep(1.0); + f_ping4(g_pars.ue_pars.run_prog_pars.ping_hostname); + f_pdu_sess_delete_tun(); +} +testcase TC_ng_register_ping4() runs on MTC_CT { + f_init(); + f_ngap_setup(0); + + var ConnHdlrPars pars := f_init_pars(ue_idx := 0); + var ConnHdlr vc_conn; + vc_conn := f_start_handler_with_pars(refers(f_TC_register_ping4), pars); + vc_conn.done; +} + control { execute( TC_ng_setup() ); execute( TC_ng_setup_unknown_global_gnb_id_plmn() ); execute( TC_ng_setup_wrong_tac() ); execute( TC_ng_register() ); + execute( TC_ng_register_ping4() ); } diff --git a/5gc/ConnHdlr.ttcn b/5gc/ConnHdlr.ttcn index 65e6c9a..2e3bedc 100644 --- a/5gc/ConnHdlr.ttcn +++ b/5gc/ConnHdlr.ttcn @@ -45,6 +45,9 @@ import from NG_NAS_Functions all; import from NG_CryptoFunctions all; +import from GTPv1U_Emulation all; +import from UECUPS_Types all; + /* (maximum) number of emulated eNBs */ const integer NUM_NGRAN := 1; @@ -55,6 +58,12 @@ SupportedTAList supported_ta_list } +type record UeDerivedKeys { + OCT32 kausf, + OCT32 kseaf, + OCT32 kamf +}; + /* parameters of emulated UE */ type record UeParams { HEX15n imsi, @@ -62,15 +71,22 @@ octetstring usim_key, octetstring usim_opc, charstring apn, + RunProgParams run_prog_pars, charstring ran_gtpu_ip, OCT4 ran_gtpu_teid, charstring cn_gtpu_ip optional, OCT4 cn_gtpu_teid optional, - charstring ue_ip optional, - OCT32 kausf optional, - OCT32 kseaf optional, - OCT32 kamf optional -} + OCT1 qfi optional, + charstring ue_ip optional +}; + +type record RunProgParams { + charstring run_as_user, + charstring log_path_prefix, + charstring tun_netns_name, + charstring tun_dev_name, + charstring ping_hostname +}; type record ConnHdlrPars { /* copied over from MTC_CT on start of component */ @@ -83,8 +99,12 @@ NAS_KeySetIdentifier kset_id } -type component ConnHdlr extends NGAP_ConnHdlr { +type component ConnHdlr extends NGAP_ConnHdlr, GTP1U_ConnHdlr { var ConnHdlrPars g_pars; + var UeDerivedKeys g_keys; + + /* number of programs started, used as identifier. */ + var integer g_start_prog_count := 0; } type function void_fn() runs on ConnHdlr; @@ -177,14 +197,14 @@ } var octetstring ssn := f_NG_NAS_ServingNetworkName_OCT(f_imsi_plmn_id(), omit); - g_pars.ue_pars.kausf := f_kdf_kausf(ck, ik, ssn, autn); - g_pars.ue_pars.kseaf := f_kdf_kseaf(g_pars.ue_pars.kausf, ssn); - g_pars.ue_pars.kamf := f_kdf_kamf(g_pars.ue_pars.kseaf, char2oct(hex2str(g_pars.ue_pars.imsi)), abba); + g_keys.kausf := f_kdf_kausf(ck, ik, ssn, autn); + g_keys.kseaf := f_kdf_kseaf(g_keys.kausf, ssn); + g_keys.kamf := f_kdf_kamf(g_keys.kseaf, char2oct(hex2str(g_pars.ue_pars.imsi)), abba); var NGAPEM_Config cfg := { set_nas_keys := { - k_nas_int := f_kdf_ng_nas_int(g_pars.ue_pars.kamf, NG_NAS_ALG_IP_NIA1), - k_nas_enc := f_kdf_ng_nas_enc(g_pars.ue_pars.kamf, NG_NAS_ALG_ENC_NEA0) + k_nas_int := f_kdf_ng_nas_int(g_keys.kamf, NG_NAS_ALG_IP_NIA1), + k_nas_enc := f_kdf_ng_nas_enc(g_keys.kamf, NG_NAS_ALG_ENC_NEA0) } }; NGAP.send(cfg); @@ -325,6 +345,7 @@ NGAP.receive(cr_NG_DL_NAS_TRANSPORT) -> value rx_nas; inner_nas := f_dec_NG_NAS_DL_Message_Payload_Container(rx_nas.dl_Nas_Transport.payloadContainerType.container, rx_nas.dl_Nas_Transport.payload.payload); + g_pars.ue_pars.qfi := inner_nas.pdu_Session_Establishment_Accept.authorizedQoSRules.listofQoSRules[0].identifier; g_pars.ue_pars.ue_ip := f_inet_ntoa(inner_nas.pdu_Session_Establishment_Accept.pduAddress.adressInfo); log("5GC assigned CN GTPU Address: ", g_pars.ue_pars.cn_gtpu_ip, " TEID: ", g_pars.ue_pars.cn_gtpu_teid); log("5GC assigned UE IP address: ", g_pars.ue_pars.ue_ip); @@ -370,4 +391,67 @@ f_pdu_sess_establish(); } +function f_pdu_sess_create_tun() runs on ConnHdlr { + var template (value) UECUPS_GtpExtHdr gtp_ext_hdr; + var template (value) UECUPS_CreateTun uecups_create; + var template (value) UECUPS_GtpExtHdr_PduSessContainer pdu_sess_cont; + + pdu_sess_cont := ts_UECUPS_GtpExtHdr_PduSessContainer(UECUPS_GtpExtHdr_PduSessContainer_Type_ul_pdu_sess_info, + oct2int(g_pars.ue_pars.qfi)); + gtp_ext_hdr := ts_UECUPS_GtpExtHdr(pdu_session_container := pdu_sess_cont); + + uecups_create := ts_UECUPS_CreateTun( + tx_teid := oct2int(g_pars.ue_pars.cn_gtpu_teid), + rx_teid := oct2int(g_pars.ue_pars.ran_gtpu_teid), + user_addr_type := IPV4, + user_addr := f_inet_addr(g_pars.ue_pars.ue_ip), + local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(g_pars.ue_pars.ran_gtpu_ip))), + remote_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(g_pars.ue_pars.cn_gtpu_ip))), + tun_dev_name := g_pars.ue_pars.run_prog_pars.tun_dev_name, + tun_netns_name := g_pars.ue_pars.run_prog_pars.tun_netns_name, + gtp_ext_hdr := gtp_ext_hdr + ); + + f_gtp1u_create_tunnel(uecups_create); +} + +/* destroy tunnel in daemon */ +function f_pdu_sess_delete_tun() runs on ConnHdlr { + var template (value) UECUPS_DestroyTun uecups_destroy; + uecups_destroy := ts_UECUPS_DestroyTun( + local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(g_pars.ue_pars.ran_gtpu_ip))), + rx_teid := oct2int(g_pars.ue_pars.ran_gtpu_teid) + ); + f_gtp1u_destroy_tunnel(uecups_destroy); +} + +private function f_run_prog_init(charstring command) runs on ConnHdlr return UECUPS_StartProgram +{ + var template (value) UECUPS_StartProgram sprog := ts_UECUPS_StartProgram( + command := command, + environment := {}, + run_as_user := g_pars.ue_pars.run_prog_pars.run_as_user, + tun_netns_name := g_pars.ue_pars.run_prog_pars.tun_netns_name + ); + return valueof(sprog); +} + +private function f_run_prog_unique_log_path() runs on ConnHdlr return charstring +{ + var charstring id := testcasename() & "-" & hex2str(g_pars.ue_pars.imsi) & "-" & int2str(g_start_prog_count); + var charstring prefix := g_pars.ue_pars.run_prog_pars.log_path_prefix & "/" & id; + g_start_prog_count := g_start_prog_count + 1; + return prefix; +} + +/* execute ping command and wait for result */ +function f_ping4(charstring host, integer interval := 1, integer count := 10) runs on ConnHdlr +{ + /* command will be filled in by f_gtp1u_ping4() below: */ + var UECUPS_StartProgram sprog := f_run_prog_init(""); + f_gtp1u_ping4(sprog, host, interval, count, g_pars.ue_pars.ue_ip, + redirect_output := true, + redirect_output_path_prefix := f_run_prog_unique_log_path()); +} + } diff --git a/5gc/expected-results.xml b/5gc/expected-results.xml index 2120ba4..01d53a1 100644 --- a/5gc/expected-results.xml +++ b/5gc/expected-results.xml @@ -4,4 +4,5 @@ <testcase classname='C5G_Tests' name='TC_ng_setup_wrong_tac' time='MASKED'/> <testcase classname='C5G_Tests' name='TC_ng_setup' time='MASKED'/> <testcase classname='C5G_Tests' name='TC_ng_register' time='MASKED'/> + <testcase classname='C5G_Tests' name='TC_ng_register_ping4' time='MASKED'/> </testsuite> diff --git a/5gc/gen_links.sh b/5gc/gen_links.sh index 43a679b..9d72550 100755 --- a/5gc/gen_links.sh +++ b/5gc/gen_links.sh @@ -18,6 +18,14 @@ FILES="IPL4asp_Functions.ttcn IPL4asp_PT.cc IPL4asp_PT.hh IPL4asp_PortType.ttcn IPL4asp_Types.ttcn IPL4asp_discovery.cc IPL4asp_protocol_L234.hh" gen_links $DIR $FILES +DIR=$BASEDIR/titan.ProtocolModules.GTP_v13.5.0/src +FILES="GTPU_EncDec.cc GTPU_Types.ttcn " +gen_links $DIR $FILES + +DIR=$BASEDIR/osmo-uecups/ttcn3 +FILES="UECUPS_CodecPort.ttcn UECUPS_CodecPort_CtrlFunct.ttcn UECUPS_CodecPort_CtrlFunctDef.cc UECUPS_Types.ttcn " +gen_links $DIR $FILES + #################### # NG_NAS start #################### @@ -80,6 +88,7 @@ FILES+="NGAP_CodecPort.ttcn NGAP_CodecPort_CtrlFunctDef.cc NGAP_CodecPort_CtrlFunct.ttcn NGAP_Functions.ttcn NGAP_Emulation.ttcn " FILES+="NG_NAS_Osmo_Types.ttcn NG_NAS_Osmo_Templates.ttcn NG_NAS_Functions.ttcn " FILES+="NG_CryptoFunctionDefs.cc NG_CryptoFunctions.ttcn " +FILES+="GTPv1U_CodecPort.ttcn GTPv1U_CodecPort_CtrlFunct.ttcn GTPv1U_CodecPort_CtrlFunctDef.cc GTPv1U_Templates.ttcn GTPv1U_Emulation.ttcnpp " gen_links $DIR $FILES gen_links_finish diff --git a/5gc/open5gs/open5gs-smf.yaml b/5gc/open5gs/open5gs-smf.yaml index 3ae7783..f925af1 100644 --- a/5gc/open5gs/open5gs-smf.yaml +++ b/5gc/open5gs/open5gs-smf.yaml @@ -36,8 +36,10 @@ session: - subnet: 10.45.0.0/16 gateway: 10.45.0.1 + dnn: internet - subnet: 2001:db8:cafe::/48 gateway: 2001:db8:cafe::1 + dnn: internet dns: - 8.8.8.8 - 8.8.4.4 diff --git a/5gc/open5gs/open5gs-upf.yaml b/5gc/open5gs/open5gs-upf.yaml index f73cbc3..c6c86d4 100644 --- a/5gc/open5gs/open5gs-upf.yaml +++ b/5gc/open5gs/open5gs-upf.yaml @@ -18,21 +18,13 @@ server: - address: 127.0.0.7 session: - - subnet: 176.16.16.0/20 - #gateway: 176.16.16.1 + - subnet: 10.45.0.0/16 + gateway: 10.45.0.1 dnn: internet - dev: ogstun4 - - subnet: 2001:780:44:2000:0:0:0:0/56 - #gateway: 2001:780:44:2000:0:0:0:1/56 - dnn: inet6 - dev: ogstun6 - - subnet: 176.16.32.0/20 - #gateway: 176.16.32.1 - dnn: inet46 - dev: ogstun4 - - subnet: 2001:780:44:2100:0:0:0:0/56 - #gateway: 2001:780:44:2100:0:0:0:1/56 - dnn: inet46 + dev: ogstun46 + - subnet: cafe::/64 + gateway: cafe::1 + dnn: internet dev: ogstun46 metrics: server: diff --git a/5gc/osmo-uecups-daemon.cfg b/5gc/osmo-uecups-daemon.cfg new file mode 100644 index 0000000..5a1eafc --- /dev/null +++ b/5gc/osmo-uecups-daemon.cfg @@ -0,0 +1,17 @@ +log gsmtap 127.0.0.202 + logging level set-all debug + logging filter all 1 +log stderr + logging filter all 1 + logging print extended-timestamp 1 + logging print file basename last + logging print category-hex 0 + logging print category 1 + logging print level 1 + logging timestamp 1 + logging color 1 + logging level set-all info +line vty + bind 127.0.0.20 +uecups + local-ip 127.0.0.20 diff --git a/5gc/regen_makefile.sh b/5gc/regen_makefile.sh index 5634e21..e3ff277 100755 --- a/5gc/regen_makefile.sh +++ b/5gc/regen_makefile.sh @@ -6,6 +6,7 @@ *.asn *.c *.ttcn + *.ttcnpp IPL4asp_PT.cc IPL4asp_discovery.cc Native_FunctionDefs.cc @@ -18,6 +19,13 @@ TCCConversion.cc TCCEncoding.cc TCCInterface.cc + GTPU_EncDec.cc + GTPv1U_CodecPort_CtrlFunctDef.cc + UECUPS_CodecPort_CtrlFunctDef.cc +" + +CPPFLAGS_TTCN3=" + -DGTP1U_EMULATION_HAVE_UECUPS " . ../_buildsystem/regen_makefile.inc.sh diff --git a/5gc/testenv.cfg b/5gc/testenv.cfg index 498c886..d70c135 100644 --- a/5gc/testenv.cfg +++ b/5gc/testenv.cfg @@ -2,6 +2,9 @@ titan_min=11.1.0 program=C5G_Tests config=C5G_Tests.cfg +copy=testsuite_prepare.sh +prepare=./testsuite_prepare.sh +clean=testenv.sh [db] prepare=mkdir db @@ -53,8 +56,7 @@ program=open5gs-upfd -c open5gs-upf.yaml make=open5gs package=open5gs-upf -copy=open5gs/open5gs-upf.yaml open5gs/testenv.sh -clean=./testenv.sh +copy=open5gs/open5gs-upf.yaml [ausf] program=open5gs-ausfd -c open5gs-ausf.yaml @@ -86,3 +88,12 @@ make=open5gs package=open5gs-amf copy=open5gs/open5gs-amf.yaml + +[uecups-daemon] +# Programs started by osmo-uecups-daemo log to this dir +prepare=mkdir run_prog +# Must run as root for writing to /var/run/netns/ +program=sudo $(which osmo-uecups-daemon) +make=osmo-uecups +package=osmo-uecups +copy=osmo-uecups-daemon.cfg diff --git a/5gc/open5gs/testenv.sh b/5gc/testenv.sh similarity index 71% rename from 5gc/open5gs/testenv.sh rename to 5gc/testenv.sh index 718dee0..25e1b71 100755 --- a/5gc/open5gs/testenv.sh +++ b/5gc/testenv.sh @@ -32,10 +32,8 @@ add_tun "ogstun6" add_tun "ogstun46" - add_addr "ogstun4" "176.16.16.1/20" - add_addr "ogstun6" "2001:780:44:2000:0:0:0:1/56" - add_addr "ogstun46" "176.16.32.1/20" - add_addr "ogstun46" "2001:780:44:2100:0:0:0:1/56" + add_addr "ogstun46" "10.45.0.1/16" + add_addr "ogstun46" "cafe::1/64" sudo ip link set ogstun4 up sudo ip link set ogstun6 up @@ -59,11 +57,6 @@ check_usage -# Add a bridge reachable through the GTP tunnel that can answer ICMP -# pings (for e.g. TC_pdp4_act_deact_gtpu_access). The bridge is also used to -# connect the SUT when it runs in QEMU. -EXTRA_IPS="172.18.3.201 fd02:db8:3::201" add_remove_testenv0_bridge.sh - case "$TESTENV_CLEAN_REASON" in prepare) setcap_open5gs_upfd diff --git a/5gc/testsuite_prepare.sh b/5gc/testsuite_prepare.sh new file mode 100755 index 0000000..052df42 --- /dev/null +++ b/5gc/testsuite_prepare.sh @@ -0,0 +1,9 @@ +#!/bin/sh -ex + +adjust_ttcn3_config() { + local as_user="${USER:-root}" + sed -i "s/^C5G_Tests.mp_run_prog_as_user := .*/C5G_Tests.mp_run_prog_as_user := \"$as_user\"/" \ + C5G_Tests.cfg +} + +adjust_ttcn3_config diff --git a/deps/Makefile b/deps/Makefile index b0b18b3..1b68262 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -124,7 +124,7 @@ titan.TestPorts.UDPasp_commit= 54176e95850654e5e8b0ffa2f1b5f35c412b949c titan.TestPorts.UNIX_DOMAIN_SOCKETasp_commit= R.2.A-13-gd34ab71 titan.TestPorts.USB_commit= a9470fdc99c82fbf988f4508c3161118c9106e28 -osmo-uecups_commit= 0eb4f08a31fd1d7aac6ffe9c6a60503f17c01787 +osmo-uecups_commit= fdff5ae0b7addf07c368adbe31610d4e57dac273 all: $(foreach dir,$(ALL_REPOS),$(dir)/update) clean: $(foreach dir,$(ALL_REPOS),$(dir)/clean) -- To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/40764?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: newchange Gerrit-Project: osmo-ttcn3-hacks Gerrit-Branch: master Gerrit-Change-Id: I66737a94eedcf4184aa59bbbd7cc700d1e9d1521 Gerrit-Change-Number: 40764 Gerrit-PatchSet: 1 Gerrit-Owner: pespin <pes...@sysmocom.de>