aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore16
-rw-r--r--configure.ac1
-rw-r--r--include/osmo-bts/phy_link.h8
-rw-r--r--src/osmo-bts-virtual/Makefile.am2
-rw-r--r--src/osmo-bts-virtual/bts_model.c65
-rw-r--r--src/osmo-bts-virtual/l1_if.c199
-rw-r--r--src/osmo-bts-virtual/l1_if.h6
-rw-r--r--src/osmo-bts-virtual/main.c27
-rw-r--r--src/osmo-bts-virtual/osmo_mcast_sock.c192
-rw-r--r--src/osmo-bts-virtual/osmo_mcast_sock.h46
-rw-r--r--src/osmo-bts-virtual/scheduler_virtbts.c318
-rw-r--r--src/osmo-bts-virtual/virtual_um.c176
-rw-r--r--src/osmo-bts-virtual/virtual_um.h16
-rw-r--r--src/osmo-bts-virtual/virtualbts_vty.c92
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/virtsock/Makefile.am15
-rw-r--r--tests/virtsock/mcast_sock.c220
-rw-r--r--tests/virtsock/mcast_sock.h40
-rw-r--r--tests/virtsock/virt_um_bts.c61
-rw-r--r--tests/virtsock/virt_um_ms.c72
-rw-r--r--tests/virtsock/virtsock_client.c57
-rw-r--r--tests/virtsock/virtsock_client_mcast.c115
-rw-r--r--tests/virtsock/virtsock_client_mcast_bidir.c51
-rw-r--r--tests/virtsock/virtsock_client_unix_domain.c31
-rw-r--r--tests/virtsock/virtsock_server.c55
-rw-r--r--tests/virtsock/virtsock_server_mcast.c48
-rw-r--r--tests/virtsock/virtsock_server_mcast_bidir.c54
-rw-r--r--tests/virtsock/virtsock_server_unix_domain.c46
28 files changed, 1640 insertions, 391 deletions
diff --git a/.gitignore b/.gitignore
index 224b77cf..447246bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,8 @@ src/osmo-bts-trx/osmo-bts-trx
src/osmo-bts-octphy/osmo-bts-octphy
+src/osmo-bts-virtual/osmo-bts-virtual
+
tests/atconfig
tests/package.m4
tests/agch/agch_test
@@ -50,6 +52,14 @@ tests/bursts/bursts_test
tests/handover/handover_test
tests/testsuite
tests/testsuite.log
+tests/virtsock/client_bidir
+tests/virtsock/server_bidir
+tests/virtsock/client_mcast
+tests/virtsock/server_mcast
+tests/virtsock/client_unix
+tests/virtsock/server_unix
+tests/virtsock/virt_um_bts
+tests/virtsock/virt_um_ms
# Possible generated file
doc/vty_reference.xml
@@ -70,3 +80,9 @@ debian/*.substvars
debian/osmo-bts-trx-dbg/
debian/osmo-bts-trx/
debian/tmp/
+
+# development environment
+/.autotools
+/.cproject
+/.project
+/.settings/
diff --git a/configure.ac b/configure.ac
index 84aa35e0..f08032cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -145,4 +145,5 @@ AC_OUTPUT(
tests/misc/Makefile
tests/bursts/Makefile
tests/handover/Makefile
+ tests/virtsock/Makefile
Makefile)
diff --git a/include/osmo-bts/phy_link.h b/include/osmo-bts/phy_link.h
index 38e7ffa8..fa01d5c1 100644
--- a/include/osmo-bts/phy_link.h
+++ b/include/osmo-bts/phy_link.h
@@ -57,9 +57,11 @@ struct phy_link {
int power_sent;
} osmotrx;
struct {
- char *mcast_group;
- char *mcast_dev;
- uint16_t mcast_port;
+ char* mcast_dev; // mcast device
+ char *bts_mcast_group; // bts will listen to this mcast grp
+ uint16_t bts_mcast_port;
+ char *ms_mcast_group; // ms will listen to this mcast grp
+ uint16_t ms_mcast_port;
struct virt_um_inst *virt_um;
} virt;
struct {
diff --git a/src/osmo-bts-virtual/Makefile.am b/src/osmo-bts-virtual/Makefile.am
index 07c6d9d3..1492d1cf 100644
--- a/src/osmo-bts-virtual/Makefile.am
+++ b/src/osmo-bts-virtual/Makefile.am
@@ -6,5 +6,5 @@ EXTRA_DIST = virtual_um.h
bin_PROGRAMS = osmo-bts-virtual
-osmo_bts_virtual_SOURCES = main.c bts_model.c virtualbts_vty.c scheduler_virtbts.c l1_if.c virtual_um.c
+osmo_bts_virtual_SOURCES = main.c bts_model.c virtualbts_vty.c scheduler_virtbts.c l1_if.c virtual_um.c osmo_mcast_sock.c
osmo_bts_virtual_LDADD = $(top_builddir)/src/common/libbts.a $(top_builddir)/src/common/libl1sched.a $(COMMON_LDADD)
diff --git a/src/osmo-bts-virtual/bts_model.c b/src/osmo-bts-virtual/bts_model.c
index 1eea26b1..fbeafa3c 100644
--- a/src/osmo-bts-virtual/bts_model.c
+++ b/src/osmo-bts-virtual/bts_model.c
@@ -22,6 +22,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/phy_link.h>
@@ -34,6 +35,20 @@
#include <osmo-bts/handover.h>
#include <osmo-bts/l1sap.h>
+// TODO: check if dummy method is sufficient, else implement
+int bts_model_lchan_deactivate(struct gsm_lchan *lchan)
+{
+ return -1;
+}
+
+// TODO: check if dummy method is sufficient, else implement
+int osmo_amr_rtp_dec(const uint8_t *rtppayload, int payload_len, uint8_t *cmr,
+ int8_t *cmi, enum osmo_amr_type *ft,
+ enum osmo_amr_quality *bfi, int8_t *sti)
+{
+ return -1;
+}
+
int bts_model_trx_close(struct gsm_bts_trx *trx)
{
return 0;
@@ -45,14 +60,29 @@ int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
}
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
- struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
- void *obj)
+ struct tlv_parsed *old_attr,
+ struct tlv_parsed *new_attr, void *obj)
{
return 0;
}
static uint8_t vbts_set_bts(struct gsm_bts *bts)
{
+ struct gsm_bts_trx *trx;
+ uint8_t tn;
+
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ {
+ oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&trx->bb_transc.mo, -1, NM_AVSTATE_OK);
+ for (tn = 0; tn < TRX_NR_TS; tn++) {
+ oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED,
+ NM_AVSTATE_DEPENDENCY);
+ }
+ // report availability of trx to the bts. this will trigger the rsl connection
+ oml_mo_tx_sw_act_rep(&trx->mo);
+ oml_mo_tx_sw_act_rep(&trx->bb_transc.mo);
+ }
return 0;
}
@@ -74,51 +104,43 @@ static uint8_t vbts_set_ts(struct gsm_bts_trx_ts *ts)
}
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
- struct tlv_parsed *new_attr, int kind, void *obj)
+ struct tlv_parsed *new_attr, int kind, void *obj)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
int cause = 0;
switch (foh->msg_type) {
- case NM_MT_SET_BTS_ATTR:
+ case NM_MT_SET_BTS_ATTR: // 0x41
cause = vbts_set_bts(obj);
break;
case NM_MT_SET_RADIO_ATTR:
- cause = vbts_set_trx(obj);
+ cause = vbts_set_trx(obj); // 0x44
break;
- case NM_MT_SET_CHAN_ATTR:
+ case NM_MT_SET_CHAN_ATTR: // 0x47
cause = vbts_set_ts(obj);
break;
}
return oml_fom_ack_nack(msg, cause);
}
-int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
- void *obj)
+// mo: the maintenance object
+int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj)
{
int rc;
switch (mo->obj_class) {
case NM_OC_RADIO_CARRIER:
case NM_OC_CHANNEL:
+ case NM_OC_SITE_MANAGER:
+ case NM_OC_BASEB_TRANSC:
+ case NM_OC_BTS:
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
rc = oml_mo_opstart_ack(mo);
break;
- case NM_OC_BTS:
- case NM_OC_SITE_MANAGER:
- case NM_OC_BASEB_TRANSC:
+ // TODO: gprs support
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
- rc = oml_mo_opstart_ack(mo);
- if (mo->obj_class == NM_OC_BTS) {
- oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK);
- }
- break;
default:
rc = oml_mo_opstart_nack(mo, NM_NACK_OBJCLASS_NOTSUPP);
}
@@ -126,7 +148,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
}
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
- void *obj, uint8_t adm_state)
+ void *obj, uint8_t adm_state)
{
mo->nm_state.administrative = adm_state;
return oml_mo_statechg_ack(mo);
@@ -137,7 +159,6 @@ int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
return 0;
}
-
int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm)
{
return 0;
diff --git a/src/osmo-bts-virtual/l1_if.c b/src/osmo-bts-virtual/l1_if.c
index 0e407bba..cc9e6712 100644
--- a/src/osmo-bts-virtual/l1_if.c
+++ b/src/osmo-bts-virtual/l1_if.c
@@ -26,6 +26,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/gsmtap.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/bts.h>
@@ -40,11 +41,73 @@
#include "virtual_um.h"
+/**
+ * Callback to handle incoming messages from the MS.
+ * The incoming message should be GSM_TAP encapsulated.
+ * TODO: implement all channels
+ */
static void virt_um_rcv_cb(struct virt_um_inst *vui, struct msgb *msg)
{
- /* FIXME: Handle msg from MS */
-}
+ struct osmo_phsap_prim l1sap;
+ struct gsmtap_hdr *gh = (struct gsmtap_hdr *)msg->l1h;
+ struct phy_link *plink = (struct phy_link *)vui->priv;
+ // TODO: is there more than one physical instance? Where do i get the corresponding pinst number? Maybe gsmtap_hdr->antenna?
+ struct phy_instance *pinst = phy_instance_by_num(plink, 0);
+
+ memset(&l1sap, 0, sizeof(l1sap));
+
+ switch (gh->sub_type) {
+ case GSMTAP_CHANNEL_RACH:
+ // generate primitive for upper layer
+ osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH,
+ PRIM_OP_INDICATION,
+ NULL);
+ l1sap.u.rach_ind.chan_nr = 0; // TODO: fill with proper value
+ l1sap.u.rach_ind.ra = 0; // TODO: fill with proper value
+ l1sap.u.rach_ind.fn = gh->frame_number;
+ // todo: schedule the uplink burst
+ //trx_sched_ul_burst(&l1h->l1s, tn, fn, bits, rssi, toa);
+ break;
+ case GSMTAP_CHANNEL_SDCCH:
+ case GSMTAP_CHANNEL_SDCCH4:
+ case GSMTAP_CHANNEL_SDCCH8:
+ // TODO: implement channel handling
+ break;
+ case GSMTAP_CHANNEL_TCH_F:
+ // TODO: implement channel handling
+ break;
+ case GSMTAP_CHANNEL_TCH_H:
+ // TODO: implement channel handling
+ break;
+ case GSMTAP_CHANNEL_AGCH:
+ case GSMTAP_CHANNEL_PCH:
+ case GSMTAP_CHANNEL_BCCH:
+ LOGP(LOGL_NOTICE, DL1P,
+ "Ignore incoming msg - channel type downlink only!");
+ goto nomessage;
+ case GSMTAP_CHANNEL_CCCH:
+ case GSMTAP_CHANNEL_PACCH:
+ case GSMTAP_CHANNEL_PDCH:
+ case GSMTAP_CHANNEL_PTCCH:
+ case GSMTAP_CHANNEL_CBCH51:
+ case GSMTAP_CHANNEL_CBCH52:
+ LOGP(LOGL_NOTICE, DL1P,
+ "Ignore incoming msg - channel type not supported!");
+ goto nomessage;
+ default:
+ LOGP(LOGL_NOTICE, DL1P,
+ "Ignore incoming msg - channel type unknown.");
+ goto nomessage;
+ }
+ /* forward primitive */
+ l1sap_up(pinst->trx, &l1sap);
+ DEBUGP(DL1P, "Message forwarded to layer 2.");
+ return;
+
+ // handle memory deallocation
+ nomessage: free(msg);
+}
/* called by common part once OML link is established */
int bts_model_oml_estab(struct gsm_bts *bts)
@@ -52,6 +115,7 @@ int bts_model_oml_estab(struct gsm_bts *bts)
return 0;
}
+/* called by bts_main to initialize physical link */
int bts_model_phy_link_open(struct phy_link *plink)
{
struct phy_instance *pinst;
@@ -63,10 +127,24 @@ int bts_model_phy_link_open(struct phy_link *plink)
phy_link_state_set(plink, PHY_LINK_CONNECTING);
- plink->u.virt.virt_um = virt_um_init(plink, plink->u.virt.mcast_group,
- plink->u.virt.mcast_port,
- plink->u.virt.mcast_dev, plink,
- virt_um_rcv_cb);
+ if (!plink->u.virt.bts_mcast_group) {
+ plink->u.virt.bts_mcast_group = DEFAULT_BTS_MCAST_GROUP;
+ }
+ if (!plink->u.virt.bts_mcast_port) {
+ plink->u.virt.bts_mcast_port = DEFAULT_BTS_MCAST_PORT;
+ }
+ if (!plink->u.virt.ms_mcast_group) {
+ plink->u.virt.ms_mcast_group = DEFAULT_MS_MCAST_GROUP;
+ }
+ if (!plink->u.virt.ms_mcast_port) {
+ plink->u.virt.ms_mcast_port = DEFAULT_MS_MCAST_PORT;
+ }
+
+ plink->u.virt.virt_um = virt_um_init(plink,
+ plink->u.virt.ms_mcast_group,
+ plink->u.virt.ms_mcast_port,
+ plink->u.virt.bts_mcast_group,
+ plink->u.virt.bts_mcast_port, virt_um_rcv_cb);
if (!plink->u.virt.virt_um) {
phy_link_state_set(plink, PHY_LINK_SHUTDOWN);
return -1;
@@ -74,8 +152,11 @@ int bts_model_phy_link_open(struct phy_link *plink)
/* iterate over list of PHY instances and initialize the
* scheduler */
- llist_for_each_entry(pinst, &plink->instances, list) {
+ llist_for_each_entry(pinst, &plink->instances, list)
+ {
trx_sched_init(&pinst->u.virt.sched, pinst->trx);
+ /* TODO: why only start scheduler for CCCH */
+ /* Only start the scheduler for the transceiver on c0. CCCH is on C0 */
if (pinst->trx == pinst->trx->bts->c0)
vbts_sched_start(pinst->trx->bts);
}
@@ -87,13 +168,13 @@ int bts_model_phy_link_open(struct phy_link *plink)
return 0;
}
-
/*
* primitive handling
*/
/* enable ciphering */
-static int l1if_set_ciphering(struct gsm_lchan *lchan, uint8_t chan_nr, int downlink)
+static int l1if_set_ciphering(struct gsm_lchan *lchan, uint8_t chan_nr,
+ int downlink)
{
struct gsm_bts_trx *trx = lchan->ts->trx;
struct phy_instance *pinst = trx_phy_instance(trx);
@@ -106,17 +187,17 @@ static int l1if_set_ciphering(struct gsm_lchan *lchan, uint8_t chan_nr, int down
if (!downlink) {
/* set uplink */
trx_sched_set_cipher(sched, chan_nr, 0, lchan->encr.alg_id - 1,
- lchan->encr.key, lchan->encr.key_len);
+ lchan->encr.key, lchan->encr.key_len);
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
} else {
/* set downlink and also set uplink, if not already */
if (lchan->ciph_state != LCHAN_CIPH_RX_CONF) {
trx_sched_set_cipher(sched, chan_nr, 0,
- lchan->encr.alg_id - 1, lchan->encr.key,
- lchan->encr.key_len);
+ lchan->encr.alg_id - 1, lchan->encr.key,
+ lchan->encr.key_len);
}
trx_sched_set_cipher(sched, chan_nr, 1, lchan->encr.alg_id - 1,
- lchan->encr.key, lchan->encr.key_len);
+ lchan->encr.key, lchan->encr.key_len);
lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
}
@@ -124,13 +205,13 @@ static int l1if_set_ciphering(struct gsm_lchan *lchan, uint8_t chan_nr, int down
}
static int mph_info_chan_confirm(struct gsm_bts_trx *trx, uint8_t chan_nr,
- enum osmo_mph_info_type type, uint8_t cause)
+ enum osmo_mph_info_type type, uint8_t cause)
{
struct osmo_phsap_prim l1sap;
memset(&l1sap, 0, sizeof(l1sap));
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM,
- NULL);
+ NULL);
l1sap.u.info.type = type;
l1sap.u.info.u.act_cnf.chan_nr = chan_nr;
l1sap.u.info.u.act_cnf.cause = cause;
@@ -144,7 +225,7 @@ int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn)
memset(&l1sap, 0, sizeof(l1sap));
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO,
- PRIM_OP_INDICATION, NULL);
+ PRIM_OP_INDICATION, NULL);
l1sap.u.info.type = PRIM_INFO_TIME;
l1sap.u.info.u.time_ind.fn = fn;
@@ -154,40 +235,42 @@ int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn)
return l1sap_up(bts->c0, &l1sap);
}
-
-static void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta,
- float ber, float rssi)
+static void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr,
+ float ta, float ber, float rssi)
{
memset(l1sap, 0, sizeof(*l1sap));
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_MPH_INFO,
- PRIM_OP_INDICATION, NULL);
+ PRIM_OP_INDICATION, NULL);
l1sap->u.info.type = PRIM_INFO_MEAS;
l1sap->u.info.u.meas_ind.chan_nr = chan_nr;
- l1sap->u.info.u.meas_ind.ta_offs_qbits = (int16_t)(ta*4);
- l1sap->u.info.u.meas_ind.ber10k = (unsigned int) (ber * 10000);
- l1sap->u.info.u.meas_ind.inv_rssi = (uint8_t) (rssi * -1);
+ l1sap->u.info.u.meas_ind.ta_offs_qbits = (int16_t)(ta * 4);
+ l1sap->u.info.u.meas_ind.ber10k = (unsigned int)(ber * 10000);
+ l1sap->u.info.u.meas_ind.inv_rssi = (uint8_t)(rssi * -1);
}
-int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr,
- int n_errors, int n_bits_total, float rssi, float toa)
+int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn,
+ uint8_t chan_nr, int n_errors, int n_bits_total,
+ float rssi, float toa)
{
struct gsm_lchan *lchan = &trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)];
struct osmo_phsap_prim l1sap;
/* 100% BER is n_bits_total is 0 */
- float ber = n_bits_total==0 ? 1.0 : (float)n_errors / (float)n_bits_total;
+ float ber = n_bits_total == 0 ?
+ 1.0 : (float)n_errors / (float)n_bits_total;
- LOGP(DMEAS, LOGL_DEBUG, "RX L1 frame %s fn=%u chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS "
- "ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n",
- gsm_lchan_name(lchan), fn, chan_nr, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
- rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info[1], lchan->rqd_ta, toa);
+ LOGP(DMEAS, LOGL_DEBUG,
+ "RX L1 frame %s fn=%u chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS "
+ "ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n",
+ gsm_lchan_name(lchan), fn, chan_nr,
+ ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
+ rssi, ber * 100, n_errors, n_bits_total,
+ lchan->meas.l1_info[1], lchan->rqd_ta, toa);
l1if_fill_meas_res(&l1sap, chan_nr, lchan->rqd_ta + toa, ber, rssi);
return l1sap_up(trx, &l1sap);
}
-
-
/* primitive from common part */
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
@@ -232,7 +315,8 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
if (l1sap->u.info.type == PRIM_INFO_ACTIVATE) {
if ((chan_nr & 0x80)) {
LOGP(DL1C, LOGL_ERROR, "Cannot activate"
- " chan_nr 0x%02x\n", chan_nr);
+ " chan_nr 0x%02x\n",
+ chan_nr);
break;
}
/* activate dedicated channel */
@@ -241,14 +325,15 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
trx_sched_set_lchan(sched, chan_nr, 0x40, 1);
/* set mode */
trx_sched_set_mode(sched, chan_nr,
- lchan->rsl_cmode, lchan->tch_mode,
- lchan->tch.amr_mr.num_modes,
- lchan->tch.amr_mr.bts_mode[0].mode,
- lchan->tch.amr_mr.bts_mode[1].mode,
- lchan->tch.amr_mr.bts_mode[2].mode,
- lchan->tch.amr_mr.bts_mode[3].mode,
- amr_get_initial_mode(lchan),
- (lchan->ho.active == 1));
+ lchan->rsl_cmode,
+ lchan->tch_mode,
+ lchan->tch.amr_mr.num_modes,
+ lchan->tch.amr_mr.bts_mode[0].mode,
+ lchan->tch.amr_mr.bts_mode[1].mode,
+ lchan->tch.amr_mr.bts_mode[2].mode,
+ lchan->tch.amr_mr.bts_mode[3].mode,
+ amr_get_initial_mode(lchan),
+ (lchan->ho.active == 1));
/* init lapdm */
lchan_init_lapdm(lchan);
/* set lchan active */
@@ -257,31 +342,32 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
l1if_set_ciphering(lchan, chan_nr, 0);
l1if_set_ciphering(lchan, chan_nr, 1);
if (lchan->encr.alg_id)
- lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
+ lchan->ciph_state =
+ LCHAN_CIPH_RXTX_CONF;
else
lchan->ciph_state = LCHAN_CIPH_NONE;
/* confirm */
mph_info_chan_confirm(trx, chan_nr,
- PRIM_INFO_ACTIVATE, 0);
+ PRIM_INFO_ACTIVATE, 0);
break;
}
if (l1sap->u.info.type == PRIM_INFO_MODIFY) {
/* change mode */
trx_sched_set_mode(sched, chan_nr,
- lchan->rsl_cmode, lchan->tch_mode,
- lchan->tch.amr_mr.num_modes,
- lchan->tch.amr_mr.bts_mode[0].mode,
- lchan->tch.amr_mr.bts_mode[1].mode,
- lchan->tch.amr_mr.bts_mode[2].mode,
- lchan->tch.amr_mr.bts_mode[3].mode,
- amr_get_initial_mode(lchan),
- 0);
+ lchan->rsl_cmode,
+ lchan->tch_mode,
+ lchan->tch.amr_mr.num_modes,
+ lchan->tch.amr_mr.bts_mode[0].mode,
+ lchan->tch.amr_mr.bts_mode[1].mode,
+ lchan->tch.amr_mr.bts_mode[2].mode,
+ lchan->tch.amr_mr.bts_mode[3].mode,
+ amr_get_initial_mode(lchan), 0);
break;
}
if ((chan_nr & 0x80)) {
LOGP(DL1C, LOGL_ERROR, "Cannot deactivate "
- "chan_nr 0x%02x\n", chan_nr);
+ "chan_nr 0x%02x\n", chan_nr);
break;
}
/* deactivate associated channel */
@@ -293,26 +379,25 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
trx_sched_set_lchan(sched, chan_nr, 0x00, 0);
/* confirm only on dedicated channel */
mph_info_chan_confirm(trx, chan_nr,
- PRIM_INFO_DEACTIVATE, 0);
+ PRIM_INFO_DEACTIVATE, 0);
lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */
}
break;
default:
LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n",
- l1sap->u.info.type);
+ l1sap->u.info.type);
rc = -EINVAL;
goto done;
}
break;
default:
LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n",
- l1sap->oph.primitive, l1sap->oph.operation);
+ l1sap->oph.primitive, l1sap->oph.operation);
rc = -EINVAL;
goto done;
}
-done:
- if (msg)
+ done: if (msg)
msgb_free(msg);
return rc;
}
diff --git a/src/osmo-bts-virtual/l1_if.h b/src/osmo-bts-virtual/l1_if.h
index 8f6d3a86..b11602f6 100644
--- a/src/osmo-bts-virtual/l1_if.h
+++ b/src/osmo-bts-virtual/l1_if.h
@@ -6,9 +6,9 @@
#include "virtual_um.h"
struct vbts_l1h {
- struct gsm_bts_trx *trx;
- struct l1sched_trx l1s;
- struct virt_um_inst *virt_um;
+ struct gsm_bts_trx *trx;
+ struct l1sched_trx l1s;
+ struct virt_um_inst *virt_um;
};
struct vbts_l1h *l1if_open(struct gsm_bts_trx *trx);
diff --git a/src/osmo-bts-virtual/main.c b/src/osmo-bts-virtual/main.c
index a27ee453..d4900d61 100644
--- a/src/osmo-bts-virtual/main.c
+++ b/src/osmo-bts-virtual/main.c
@@ -76,12 +76,10 @@ int bts_model_handle_options(int argc, char **argv)
while (1) {
int option_idx = 0, c;
static const struct option long_options[] = {
- /* specific to this hardware */
- { 0, 0, 0, 0 }
- };
+ /* specific to this hardware */
+ {0, 0, 0, 0}};
- c = getopt_long(argc, argv, "",
- long_options, &option_idx);
+ c = getopt_long(argc, argv, "", long_options, &option_idx);
if (c == -1)
break;
@@ -101,6 +99,25 @@ void bts_model_abis_close(struct gsm_bts *bts)
bts_shutdown(bts, "Abis close");
}
+void bts_model_phy_link_set_defaults(struct phy_link *plink)
+{
+}
+
+void bts_model_phy_instance_set_defaults(struct phy_instance *pinst)
+{
+}
+
+int bts_model_ts_disconnect(struct gsm_bts_trx_ts *ts)
+{
+ return -ENOTSUP;
+}
+
+int bts_model_ts_connect(struct gsm_bts_trx_ts *ts,
+ enum gsm_phys_chan_config as_pchan)
+{
+ return -ENOTSUP;
+}
+
int main(int argc, char **argv)
{
return bts_main(argc, argv);
diff --git a/src/osmo-bts-virtual/osmo_mcast_sock.c b/src/osmo-bts-virtual/osmo_mcast_sock.c
new file mode 100644
index 00000000..c1777347
--- /dev/null
+++ b/src/osmo-bts-virtual/osmo_mcast_sock.c
@@ -0,0 +1,192 @@
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/select.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <talloc.h>
+#include <unistd.h>
+
+#include "osmo_mcast_sock.h"
+
+struct mcast_server_sock *mcast_server_sock_setup(void *ctx,
+ char* tx_mcast_group,
+ int tx_mcast_port,
+ int loopback)
+{
+ struct mcast_server_sock *serv_sock = talloc_zero(ctx,
+ struct mcast_server_sock);
+
+ serv_sock->osmo_fd = talloc_zero(ctx, struct osmo_fd);
+ serv_sock->sock_conf = talloc_zero(ctx, struct sockaddr_in);
+
+ // setup mcast server socket
+ serv_sock->osmo_fd->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (serv_sock->osmo_fd->fd == -1) {
+ perror("Failed to create Multicast Server Socket");
+ return NULL;
+ }
+
+ serv_sock->sock_conf->sin_family = AF_INET;
+ serv_sock->sock_conf->sin_addr.s_addr = inet_addr(tx_mcast_group);
+ serv_sock->sock_conf->sin_port = htons(tx_mcast_port);
+
+ // determines whether sent mcast packets should be looped back to the local sockets.
+ // loopback must be enabled if the mcast client is on the same machine
+ if (setsockopt(serv_sock->osmo_fd->fd, IPPROTO_IP,
+ IP_MULTICAST_LOOP, &loopback, sizeof(loopback)) < 0) {
+ perror("Failed to disable loopback.\n");
+ return NULL;
+ }
+
+ return serv_sock;
+}
+
+struct mcast_client_sock *mcast_client_sock_setup(
+ void *ctx, char* mcast_group, int mcast_port,
+ int (*fd_rx_cb)(struct osmo_fd *ofd, unsigned int what),
+ void *osmo_fd_data)
+{
+ struct mcast_client_sock *client_sock = talloc_zero(ctx,
+ struct mcast_client_sock);
+ struct sockaddr_in *rx_sock_conf = talloc_zero(NULL,
+ struct sockaddr_in);
+ int rc, reuseaddr = 1, loopback = 1;
+
+ client_sock->osmo_fd = talloc_zero(ctx, struct osmo_fd);
+ client_sock->mcast_group = talloc_zero(ctx, struct ip_mreq);
+
+ // Create mcast client socket
+ client_sock->osmo_fd->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (client_sock->osmo_fd->fd == -1) {
+ perror("Could not create mcast client socket");
+ return NULL;
+ }
+
+ // Enable SO_REUSEADDR to allow multiple instances of this application to receive copies of the multicast datagrams.
+ rc = setsockopt(client_sock->osmo_fd->fd,
+ SOL_SOCKET,
+ SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
+ if (rc < 0) {
+ perror("Failed to configure REUSEADDR option");
+ return NULL;
+ }
+
+ // Bind to the proper port number with the IP address specified as INADDR_ANY.
+ rx_sock_conf->sin_family = AF_INET;
+ rx_sock_conf->sin_addr.s_addr = htonl(INADDR_ANY);
+ rx_sock_conf->sin_port = htons(mcast_port);
+ rc = bind(client_sock->osmo_fd->fd, (struct sockaddr *)rx_sock_conf,
+ sizeof(*rx_sock_conf));
+ talloc_free(rx_sock_conf);
+ if (rc < 0) {
+ perror("Could not bind mcast client socket");
+ return NULL;
+ }
+
+ // Enable loopback of msgs to the host.
+ // Loopback must be enabled for the client, so multiple processes are able to recevie a mcast package.
+ rc = setsockopt(client_sock->osmo_fd->fd,
+ IPPROTO_IP,
+ IP_MULTICAST_LOOP, &loopback, sizeof(loopback));
+ if (rc < 0) {
+ perror("Failed to enable IP_MULTICAST_LOOP");
+ return NULL;
+ }
+
+ // Configure and join the multicast group
+ client_sock->mcast_group->imr_multiaddr.s_addr = inet_addr(mcast_group);
+ client_sock->mcast_group->imr_interface.s_addr = htonl(INADDR_ANY);
+ rc = setsockopt(client_sock->osmo_fd->fd,
+ IPPROTO_IP,
+ IP_ADD_MEMBERSHIP, client_sock->mcast_group,
+ sizeof(*client_sock->mcast_group));
+ if (rc < 0) {
+ perror("Failed to join to mcast goup");
+ return NULL;
+ }
+
+ // configure and register the osmocom filedescriptor
+ client_sock->osmo_fd->cb = fd_rx_cb;
+ client_sock->osmo_fd->when = BSC_FD_READ;
+ client_sock->osmo_fd->data = osmo_fd_data;
+
+ osmo_fd_register(client_sock->osmo_fd);
+
+ return client_sock;
+}
+
+struct mcast_bidir_sock *mcast_bidir_sock_setup(
+ void *ctx, char* tx_mcast_group, int tx_mcast_port,
+ char* rx_mcast_group, int rx_mcast_port, int loopback,
+ int (*fd_rx_cb)(struct osmo_fd *ofd, unsigned int what),
+ void *osmo_fd_data)
+{
+ struct mcast_bidir_sock *bidir_sock = talloc(ctx,
+ struct mcast_bidir_sock);
+ bidir_sock->rx_sock = mcast_client_sock_setup(ctx, rx_mcast_group,
+ rx_mcast_port, fd_rx_cb, osmo_fd_data);
+ bidir_sock->tx_sock = mcast_server_sock_setup(ctx, tx_mcast_group,
+ tx_mcast_port, loopback);
+ if (!bidir_sock->rx_sock || !bidir_sock->tx_sock) {
+ return NULL;
+ }
+ return bidir_sock;
+
+}
+
+int mcast_client_sock_rx(struct mcast_client_sock *client_sock, void* buf,
+ int buf_len)
+{
+ return recv(client_sock->osmo_fd->fd, buf, buf_len, 0);
+}
+
+int mcast_server_sock_tx(struct mcast_server_sock *serv_sock, void* data,
+ int data_len)
+{
+ return sendto(serv_sock->osmo_fd->fd, data, data_len, 0,
+ (struct sockaddr *)serv_sock->sock_conf,
+ sizeof(*serv_sock->sock_conf));
+}
+
+int mcast_bidir_sock_tx(struct mcast_bidir_sock *bidir_sock, void* data,
+ int data_len)
+{
+ return mcast_server_sock_tx(bidir_sock->tx_sock, data, data_len);
+}
+int mcast_bidir_sock_rx(struct mcast_bidir_sock *bidir_sock, void* buf,
+ int buf_len)
+{
+ return mcast_client_sock_rx(bidir_sock->rx_sock, buf, buf_len);
+}
+
+void mcast_client_sock_close(struct mcast_client_sock *client_sock)
+{
+ setsockopt(client_sock->osmo_fd->fd,
+ IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, client_sock->mcast_group,
+ sizeof(*client_sock->mcast_group));
+ osmo_fd_unregister(client_sock->osmo_fd);
+ client_sock->osmo_fd->fd = -1;
+ client_sock->osmo_fd->when = 0;
+ close(client_sock->osmo_fd->fd);
+ talloc_free(client_sock->mcast_group);
+ talloc_free(client_sock->osmo_fd);
+ talloc_free(client_sock);
+
+}
+void mcast_server_sock_close(struct mcast_server_sock *serv_sock)
+{
+ close(serv_sock->osmo_fd->fd);
+ talloc_free(serv_sock->sock_conf);
+ talloc_free(serv_sock);
+}
+
+void mcast_bidir_sock_close(struct mcast_bidir_sock *bidir_sock)
+{
+ mcast_client_sock_close(bidir_sock->rx_sock);
+ mcast_server_sock_close(bidir_sock->tx_sock);
+ talloc_free(bidir_sock);
+}
diff --git a/src/osmo-bts-virtual/osmo_mcast_sock.h b/src/osmo-bts-virtual/osmo_mcast_sock.h
new file mode 100644
index 00000000..f318ffea
--- /dev/null
+++ b/src/osmo-bts-virtual/osmo_mcast_sock.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <netinet/in.h>
+#include <osmocom/core/select.h>
+
+struct mcast_server_sock {
+ struct osmo_fd *osmo_fd;
+ struct sockaddr_in *sock_conf;
+};
+
+struct mcast_client_sock {
+ struct osmo_fd *osmo_fd;
+ struct ip_mreq *mcast_group;
+};
+
+struct mcast_bidir_sock {
+ struct mcast_server_sock *tx_sock;
+ struct mcast_client_sock *rx_sock;
+};
+
+struct mcast_bidir_sock *mcast_bidir_sock_setup(
+ void *ctx, char* tx_mcast_group, int tx_mcast_port,
+ char* rx_mcast_group, int rx_mcast_port, int loopback,
+ int (*fd_rx_cb)(struct osmo_fd *ofd, unsigned int what),
+ void *osmo_fd_data);
+
+struct mcast_server_sock *mcast_server_sock_setup(void *ctx,
+ char* tx_mcast_group,
+ int tx_mcast_port,
+ int loopback);
+struct mcast_client_sock *mcast_client_sock_setup(
+ void *ctx, char* mcast_group, int mcast_port,
+ int (*fd_rx_cb)(struct osmo_fd *ofd, unsigned int what),
+ void *osmo_fd_data);
+int mcast_client_sock_rx(struct mcast_client_sock *client_sock, void* buf,
+ int buf_len);
+int mcast_server_sock_tx(struct mcast_server_sock *serv_sock, void* data,
+ int data_len);
+int mcast_bidir_sock_tx(struct mcast_bidir_sock *bidir_sock, void* data,
+ int data_len);
+int mcast_bidir_sock_rx(struct mcast_bidir_sock *bidir_sock, void* buf,
+ int buf_len);
+void mcast_client_sock_close(struct mcast_client_sock* client_sock);
+void mcast_server_sock_close(struct mcast_server_sock* server_sock);
+void mcast_bidir_sock_close(struct mcast_bidir_sock* bidir_sock);
+
diff --git a/src/osmo-bts-virtual/scheduler_virtbts.c b/src/osmo-bts-virtual/scheduler_virtbts.c
index c23632c5..0531ef61 100644
--- a/src/osmo-bts-virtual/scheduler_virtbts.c
+++ b/src/osmo-bts-virtual/scheduler_virtbts.c
@@ -28,6 +28,8 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/gsm/rsl.h>
#include <osmocom/netif/rtp.h>
@@ -44,23 +46,48 @@
extern void *tall_bts_ctx;
-
-
+/**
+ * Send a message over the virtual um interface.
+ * This will at first wrap the msg with a gsmtap header and then write it to the declared multicast socket.
+ */
static void tx_to_virt_um(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, struct msgb *msg)
+ enum trx_chan_type chan, struct msgb *msg)
{
const struct trx_chan_desc *chdesc = &trx_chan_desc[chan];
- uint8_t ss = 0; //FIXME(chdesc);
- uint8_t gsmtap_chan;
+ uint8_t chan_type, timeslot, subslot, signal_dbm, signal_snr;
+ rsl_dec_chan_nr(chdesc->chan_nr, &chan_type, &subslot, &timeslot);
+ if (chan_type == RSL_CHAN_PCH_AGCH) {
+ if (L1SAP_FN2CCCHBLOCK(fn) >= 1) {
+ chan_type = GSMTAP_CHANNEL_PCH;
+ } else {
+ chan_type = GSMTAP_CHANNEL_AGCH;
+ }
+ } else {
+ chan_type = chantype_rsl2gsmtap(chan_type, chdesc->link_id); // the logical channel type
+ }
+ //uint8_t timeslot = tn; // indicates the physical channel
+ //uint8_t subslot = 0; // indicates the logical channel subslot on the physical channel FIXME: calculate
+ signal_dbm = 63; // the signal strength is not needed in virt phy
+ signal_snr = 40; // the signal to noice ratio is not needed in virt phy
+ uint8_t *data = msgb_l2(msg); // data bits to transmit (whole message without l1 header)
+ uint8_t data_len = msgb_l2len(msg);
struct msgb *outmsg;
- gsmtap_chan = chantype_rsl2gsmtap(chdesc->chan_nr, chdesc->link_id);
- outmsg = gsmtap_makemsg(l1t->trx->arfcn, tn, gsmtap_chan, ss, fn,
- 0, 0, msgb_l2(msg), msgb_l2len(msg));
+ // TODO: encrypt and encode message data
+
+ outmsg = gsmtap_makemsg(l1t->trx->arfcn, timeslot, chan_type, subslot,
+ fn, signal_dbm, signal_snr, data, data_len);
if (outmsg) {
struct phy_instance *pinst = trx_phy_instance(l1t->trx);
struct virt_um_inst *virt_um = pinst->phy_link->u.virt.virt_um;
- virt_um_write_msg(virt_um, outmsg);
+ if (virt_um_write_msg(virt_um, outmsg) == -1) {
+ struct gsmtap_hdr *gh = (struct gsmtap_hdr *)msgb_data(
+ outmsg);
+ LOGP(DL1C, LOGL_ERROR,
+ "Message could not be written to virtual UM! arfcn = %u gsmtap_chan=%u fn=%u ts=%u\n",
+ gh->arfcn, gh->type, gh->frame_number,
+ gh->timeslot);
+ }
}
/* free message */
@@ -73,62 +100,67 @@ static void tx_to_virt_um(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
/* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */
ubit_t *tx_idle_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
return NULL;
}
ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
return NULL;
}
ubit_t *tx_sch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
return NULL;
}
ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
struct msgb *msg;
+ if (fn == 57) {
+ DEBUGP(DL1P, "lol");
+ }
+
if (bid > 0)
return NULL;
/* get mac block from queue */
+ // what queue is that and who does fill it?
msg = _sched_dequeue_prim(l1t, tn, fn, chan);
if (msg)
goto got_msg;
LOGP(DL1P, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
-no_msg:
- return NULL;
+ no_msg: return NULL;
-got_msg:
+ got_msg:
/* check validity of message */
if (msgb_l2len(msg) != GSM_MACBLOCK_LEN) {
LOGP(DL1P, LOGL_FATAL, "Prim not 23 bytes, please FIX! "
- "(len=%d)\n", msgb_l2len(msg));
+ "(len=%d)\n", msgb_l2len(msg));
/* free message */
msgb_free(msg);
goto no_msg;
}
+ // transmit the msg received on dl from bsc to layer1 (virt um)
tx_to_virt_um(l1t, tn, fn, chan, msg);
return NULL;
}
ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
@@ -144,21 +176,20 @@ ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
goto got_msg;
LOGP(DL1P, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
-no_msg:
- return NULL;
+ no_msg: return NULL;
-got_msg:
- tx_to_virt_um(l1t, tn, fn, chan, msg);
+ got_msg: tx_to_virt_um(l1t, tn, fn, chan, msg);
return NULL;
}
static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch,
- struct msgb **_msg_facch, int codec_mode_request)
+ enum trx_chan_type chan, uint8_t bid,
+ struct msgb **_msg_tch, struct msgb **_msg_facch,
+ int codec_mode_request)
{
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL;
@@ -169,16 +200,16 @@ static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
#if 0
/* handle loss detection of received TCH frames */
if (rsl_cmode == RSL_CMOD_SPD_SPEECH
- && ++(chan_state->lost) > 5) {
+ && ++(chan_state->lost) > 5) {
uint8_t tch_data[GSM_FR_BYTES];
int len;
LOGP(DL1P, LOGL_NOTICE, "Missing TCH bursts detected, sending "
- "BFI for %s\n", trx_chan_desc[chan].name);
+ "BFI for %s\n", trx_chan_desc[chan].name);
/* indicate bad frame */
switch (tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* FR / HR */
+ case GSM48_CMODE_SPEECH_V1: /* FR / HR */
if (chan != TRXC_TCHF) { /* HR */
tch_data[0] = 0x70; /* F = 0, FT = 111 */
memset(tch_data + 1, 0, 14);
@@ -188,29 +219,29 @@ static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
memset(tch_data, 0, GSM_FR_BYTES);
len = GSM_FR_BYTES;
break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
if (chan != TRXC_TCHF)
- goto inval_mode1;
+ goto inval_mode1;
memset(tch_data, 0, GSM_EFR_BYTES);
len = GSM_EFR_BYTES;
break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
len = amr_compose_payload(tch_data,
- chan_state->codec[chan_state->dl_cmr],
- chan_state->codec[chan_state->dl_ft], 1);
+ chan_state->codec[chan_state->dl_cmr],
+ chan_state->codec[chan_state->dl_ft], 1);
if (len < 2)
- break;
+ break;
memset(tch_data + 2, 0, len - 2);
_sched_compose_tch_ind(l1t, tn, 0, chan, tch_data, len);
break;
- default:
-inval_mode1:
+ default:
+ inval_mode1:
LOGP(DL1P, LOGL_ERROR, "TCH mode invalid, please "
- "fix!\n");
+ "fix!\n");
len = 0;
}
if (len)
- _sched_compose_tch_ind(l1t, tn, 0, chan, tch_data, len);
+ _sched_compose_tch_ind(l1t, tn, 0, chan, tch_data, len);
}
#endif
@@ -225,7 +256,7 @@ inval_mode1:
l1sap = msgb_l1sap_prim(msg2);
if (l1sap->oph.primitive == PRIM_TCH) {
LOGP(DL1P, LOGL_FATAL, "TCH twice, "
- "please FIX! ");
+ "please FIX! ");
msgb_free(msg2);
} else
msg_facch = msg2;
@@ -236,7 +267,7 @@ inval_mode1:
l1sap = msgb_l1sap_prim(msg2);
if (l1sap->oph.primitive != PRIM_TCH) {
LOGP(DL1P, LOGL_FATAL, "FACCH twice, "
- "please FIX! ");
+ "please FIX! ");
msgb_free(msg2);
} else
msg_tch = msg2;
@@ -253,7 +284,7 @@ inval_mode1:
/* check validity of message */
if (msg_facch && msgb_l2len(msg_facch) != GSM_MACBLOCK_LEN) {
LOGP(DL1P, LOGL_FATAL, "Prim not 23 bytes, please FIX! "
- "(len=%d)\n", msgb_l2len(msg_facch));
+ "(len=%d)\n", msgb_l2len(msg_facch));
/* free message */
msgb_free(msg_facch);
msg_facch = NULL;
@@ -266,10 +297,12 @@ inval_mode1:
int cmr, ft, i;
if (rsl_cmode != RSL_CMOD_SPD_SPEECH) {
- LOGP(DL1P, LOGL_NOTICE, "%s Dropping speech frame, "
- "because we are not in speech mode trx=%u "
- "ts=%u at fn=%u.\n", trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ LOGP(DL1P, LOGL_NOTICE,
+ "%s Dropping speech frame, "
+ "because we are not in speech mode trx=%u "
+ "ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr,
+ tn, fn);
goto free_bad_msg;
}
@@ -278,24 +311,27 @@ inval_mode1:
if (chan != TRXC_TCHF) { /* HR */
len = 15;
if (msgb_l2len(msg_tch) >= 1
- && (msg_tch->l2h[0] & 0xf0) != 0x00) {
- LOGP(DL1P, LOGL_NOTICE, "%s "
- "Transmitting 'bad "
- "HR frame' trx=%u ts=%u at "
- "fn=%u.\n",
- trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ && (msg_tch->l2h[0] & 0xf0)
+ != 0x00) {
+ LOGP(DL1P, LOGL_NOTICE,
+ "%s "
+ "Transmitting 'bad "
+ "HR frame' trx=%u ts=%u at "
+ "fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1t->trx->nr, tn, fn);
goto free_bad_msg;
}
break;
}
len = GSM_FR_BYTES;
if (msgb_l2len(msg_tch) >= 1
- && (msg_tch->l2h[0] >> 4) != 0xd) {
- LOGP(DL1P, LOGL_NOTICE, "%s Transmitting 'bad "
- "FR frame' trx=%u ts=%u at fn=%u.\n",
- trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ && (msg_tch->l2h[0] >> 4) != 0xd) {
+ LOGP(DL1P, LOGL_NOTICE,
+ "%s Transmitting 'bad "
+ "FR frame' trx=%u ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1t->trx->nr, tn, fn);
goto free_bad_msg;
}
break;
@@ -304,26 +340,27 @@ inval_mode1:
goto inval_mode2;
len = GSM_EFR_BYTES;
if (msgb_l2len(msg_tch) >= 1
- && (msg_tch->l2h[0] >> 4) != 0xc) {
- LOGP(DL1P, LOGL_NOTICE, "%s Transmitting 'bad "
- "EFR frame' trx=%u ts=%u at fn=%u.\n",
- trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ && (msg_tch->l2h[0] >> 4) != 0xc) {
+ LOGP(DL1P, LOGL_NOTICE,
+ "%s Transmitting 'bad "
+ "EFR frame' trx=%u ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1t->trx->nr, tn, fn);
goto free_bad_msg;
}
break;
case GSM48_CMODE_SPEECH_AMR: /* AMR */
#if 0
len = amr_decompose_payload(msg_tch->l2h,
- msgb_l2len(msg_tch), &cmr_codec, &ft_codec,
- &bfi);
+ msgb_l2len(msg_tch), &cmr_codec, &ft_codec,
+ &bfi);
cmr = -1;
ft = -1;
for (i = 0; i < chan_state->codecs; i++) {
if (chan_state->codec[i] == cmr_codec)
- cmr = i;
+ cmr = i;
if (chan_state->codec[i] == ft_codec)
- ft = i;
+ ft = i;
}
if (cmr >= 0) { /* new request */
chan_state->dl_cmr = cmr;
@@ -335,46 +372,47 @@ inval_mode1:
}
if (ft < 0) {
LOGP(DL1P, LOGL_ERROR, "%s Codec (FT = %d) "
- " of RTP frame not in list. "
- "trx=%u ts=%u\n",
- trx_chan_desc[chan].name, ft_codec,
- l1t->trx->nr, tn);
+ " of RTP frame not in list. "
+ "trx=%u ts=%u\n",
+ trx_chan_desc[chan].name, ft_codec,
+ l1t->trx->nr, tn);
goto free_bad_msg;
}
if (codec_mode_request && chan_state->dl_ft != ft) {
LOGP(DL1P, LOGL_NOTICE, "%s Codec (FT = %d) "
- " of RTP cannot be changed now, but in "
- "next frame. trx=%u ts=%u\n",
- trx_chan_desc[chan].name, ft_codec,
- l1t->trx->nr, tn);
+ " of RTP cannot be changed now, but in "
+ "next frame. trx=%u ts=%u\n",
+ trx_chan_desc[chan].name, ft_codec,
+ l1t->trx->nr, tn);
goto free_bad_msg;
}
chan_state->dl_ft = ft;
if (bfi) {
LOGP(DL1P, LOGL_NOTICE, "%s Transmitting 'bad "
- "AMR frame' trx=%u ts=%u at fn=%u.\n",
- trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ "AMR frame' trx=%u ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1t->trx->nr, tn, fn);
goto free_bad_msg;
}
#endif
break;
default:
-inval_mode2:
+ inval_mode2:
LOGP(DL1P, LOGL_ERROR, "TCH mode invalid, please "
- "fix!\n");
+ "fix!\n");
goto free_bad_msg;
}
if (len < 0) {
LOGP(DL1P, LOGL_ERROR, "Cannot send invalid AMR "
- "payload\n");
+ "payload\n");
goto free_bad_msg;
}
if (msgb_l2len(msg_tch) != len) {
- LOGP(DL1P, LOGL_ERROR, "Cannot send payload with "
- "invalid length! (expecing %d, received %d)\n",
- len, msgb_l2len(msg_tch));
-free_bad_msg:
+ LOGP(DL1P, LOGL_ERROR,
+ "Cannot send payload with "
+ "invalid length! (expecing %d, received %d)\n",
+ len, msgb_l2len(msg_tch));
+ free_bad_msg:
/* free message */
msgb_free(msg_tch);
msg_tch = NULL;
@@ -382,13 +420,12 @@ free_bad_msg:
}
}
-send_frame:
- *_msg_tch = msg_tch;
+ send_frame: *_msg_tch = msg_tch;
*_msg_facch = msg_facch;
}
ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
struct msgb *msg_tch = NULL, *msg_facch = NULL;
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
@@ -400,13 +437,13 @@ ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
return NULL;
tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch,
- (((fn + 4) % 26) >> 2) & 1);
+ (((fn + 4) % 26) >> 2) & 1);
/* no message at all */
if (!msg_tch && !msg_facch) {
LOGP(DL1P, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
goto send_burst;
}
@@ -416,13 +453,13 @@ ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
} else
tx_to_virt_um(l1t, tn, fn, chan, msg_tch);
-send_burst:
+ send_burst:
return NULL;
}
ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
struct msgb *msg_tch = NULL, *msg_facch = NULL;
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
@@ -436,13 +473,13 @@ ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
/* get TCH and/or FACCH */
tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch,
- (((fn + 4) % 26) >> 2) & 1);
+ (((fn + 4) % 26) >> 2) & 1);
/* check for FACCH alignment */
if (msg_facch && ((((fn + 4) % 26) >> 2) & 1)) {
LOGP(DL1P, LOGL_ERROR, "%s Cannot transmit FACCH starting on "
- "even frames, please fix RTS!\n",
- trx_chan_desc[chan].name);
+ "even frames, please fix RTS!\n",
+ trx_chan_desc[chan].name);
msgb_free(msg_facch);
msg_facch = NULL;
}
@@ -450,8 +487,8 @@ ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
/* no message at all */
if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) {
LOGP(DL1P, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
goto send_burst;
}
@@ -461,11 +498,9 @@ ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
} else
tx_to_virt_um(l1t, tn, fn, chan, msg_tch);
-send_burst:
- return NULL;
+ send_burst: return NULL;
}
-
/***********************************************************************
* RX on uplink (indication to upper layer)
***********************************************************************/
@@ -475,42 +510,43 @@ send_burst:
* towards receiving bursts */
int rx_rach_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
/*! \brief a single burst was received by the PHY, process it */
int rx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
int rx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
int rx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
int rx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
-void _sched_act_rach_det(struct l1sched_trx *l1t, uint8_t tn, uint8_t ss, int activate)
+void _sched_act_rach_det(struct l1sched_trx *l1t, uint8_t tn, uint8_t ss,
+ int activate)
{
}
@@ -524,22 +560,36 @@ void _sched_act_rach_det(struct l1sched_trx *l1t, uint8_t tn, uint8_t ss, int ac
static int vbts_sched_fn(struct gsm_bts *bts, uint32_t fn)
{
struct gsm_bts_trx *trx;
+ uint16_t nbits;
/* send time indication */
+ // update model with new frame number, lot of stuff happening, mesurements of timeslots
+ // saving gsm time in bts model, and more
l1if_mph_time_ind(bts, fn);
/* advance the frame number? */
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
+ // is this the time advance functionality that depends of the distance to the ms???
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ {
struct phy_instance *pinst = trx_phy_instance(trx);
struct l1sched_trx *l1t = &pinst->u.virt.sched;
int tn;
-
+ const ubit_t *bits;
+ // do for each of the 8 timeslots
for (tn = 0; tn < ARRAY_SIZE(l1t->ts); tn++) {
- /* Generate RTS.ind to higher layers */
- _sched_rts(l1t, tn, (fn + RTS_ADVANCE) % GSM_HYPERFRAME);
+ /* Generate RTS indication to higher layers */
+ // ST: This will basically do 2 things (check l1_if:bts_model_l1sap_down):
+ // 1) Get pending messages from layer 2 (from the lapdm queue)
+ // 2) Process the messages
+ // --> Handle and process non-transparent RSL-Messages (activate channel, )
+ // --> Forward transparent RSL-DATA-Messages to the ms by appending them to the l1-dl-queue
+ _sched_rts(l1t, tn,
+ (fn + RTS_ADVANCE) % GSM_HYPERFRAME);
/* schedule transmit backend functions */
- _sched_dl_burst(l1t, tn, fn);
+ // ST: Process data in the l1-dlqueue and forward it to ms
+ // the returned bits are not used here, the routines called will directly forward their bits to the virt um
+ bits = _sched_dl_burst(l1t, tn, fn, &nbits);
+
}
}
@@ -556,25 +606,34 @@ static void vbts_fn_timer_cb(void *data)
gettimeofday(&tv_now, NULL);
- elapsed_us = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000 +
- (tv_now.tv_usec - tv_clock->tv_usec);
+ // check how much time elapsed till the last timer callback call
+ // this value should be about 4.615 ms (a bit greater) as this is the scheduling interval
+ elapsed_us = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
+ + (tv_now.tv_usec - tv_clock->tv_usec);
- if (elapsed_us > 2*FRAME_DURATION_uS)
- LOGP(DL1P, LOGL_NOTICE, "vbts_fn_timer_cb after %d us\n", elapsed_us);
+ // not so good somehow a lot of time passed between two timer callbacks
+ if (elapsed_us > 2 * FRAME_DURATION_uS)
+ LOGP(DL1P, LOGL_NOTICE, "vbts_fn_timer_cb after %d us\n",
+ elapsed_us);
+ // schedule the current frame/s (fn = frame number)
+ // this loop will be called at least once, but can also be executed
+ // multiple times if more than one frame duration (4615us) passed till the last callback
while (elapsed_us > FRAME_DURATION_uS / 2) {
- const struct timeval tv_frame = {
- .tv_sec = 0,
- .tv_usec = FRAME_DURATION_uS,
- };
+ const struct timeval tv_frame = {.tv_sec = 0, .tv_usec =
+ FRAME_DURATION_uS, };
timeradd(tv_clock, &tv_frame, tv_clock);
+ // increment the frame number in the bts model instance
btsb->vbts.last_fn = (btsb->vbts.last_fn + 1) % GSM_HYPERFRAME;
vbts_sched_fn(bts, btsb->vbts.last_fn);
elapsed_us -= FRAME_DURATION_uS;
}
/* re-schedule the timer */
- osmo_timer_schedule(&btsb->vbts.fn_timer, 0, FRAME_DURATION_uS - elapsed_us);
+ // timer is set to frame duration - elapsed time to guarantee that this cb method will be
+ // periodically executed every 4.615ms
+ osmo_timer_schedule(&btsb->vbts.fn_timer, 0,
+ FRAME_DURATION_uS - elapsed_us);
}
int vbts_sched_start(struct gsm_bts *bts)
@@ -588,6 +647,7 @@ int vbts_sched_start(struct gsm_bts *bts)
btsb->vbts.fn_timer.data = bts;
gettimeofday(&btsb->vbts.tv_clock, NULL);
+ // trigger the first timer after 4615us (a frame duration)
osmo_timer_schedule(&btsb->vbts.fn_timer, 0, FRAME_DURATION_uS);
return 0;
diff --git a/src/osmo-bts-virtual/virtual_um.c b/src/osmo-bts-virtual/virtual_um.c
index 2126b092..e2e86e44 100644
--- a/src/osmo-bts-virtual/virtual_um.c
+++ b/src/osmo-bts-virtual/virtual_um.c
@@ -19,118 +19,44 @@
*
*/
-#include <unistd.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <net/if.h>
-
#include <osmocom/core/select.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
#include "virtual_um.h"
+#include "osmo_mcast_sock.h"
-#define VIRT_UM_MSGB_SIZE 256
-
-static int mcast_join_group(int fd, const char *group, const char *netdev)
-{
- int ifindex = 0;
- int rc, af;
- socklen_t af_len = sizeof(af);
-
- if (netdev) {
- ifindex = if_nametoindex(netdev);
- if (!ifindex)
- return -1;
- }
-
- rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &af_len);
- if (rc < 0)
- return rc;
-
- switch (af) {
- case AF_INET:
- {
- struct ip_mreqn mr;
- memset(&mr, 0, sizeof(mr));
- inet_pton(AF_INET, group, &mr.imr_multiaddr);
- if (ifindex)
- mr.imr_ifindex = ifindex;
- rc = setsockopt(fd, SOL_SOCKET, IP_ADD_MEMBERSHIP,
- &mr, sizeof(mr));
- }
- break;
- case AF_INET6:
- {
- struct ipv6_mreq mr;
- memset(&mr, 0, sizeof(mr));
- inet_pton(AF_INET6, group, &mr.ipv6mr_multiaddr);
- if (ifindex)
- mr.ipv6mr_interface = ifindex;
- rc = setsockopt(fd, SOL_SOCKET, IPV6_ADD_MEMBERSHIP,
- &mr, sizeof(mr));
-
- }
- break;
- default:
- rc = -1;
- break;
- }
-
- return rc;
-}
-
-static int mcast_connect(int fd, const char *group, uint16_t port)
-{
- int rc, af;
- socklen_t af_len = sizeof(af);
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
-
- rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &af_len);
- if (rc < 0)
- return rc;
-
- switch (af) {
- case AF_INET:
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port);
- inet_pton(AF_INET, group, &sin.sin_addr);
- rc = connect(fd, (struct sockaddr *) &sin, sizeof(sin));
- break;
- case AF_INET6:
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
- sin6.sin6_port = htons(port);
- inet_pton(AF_INET6, group, &sin6.sin6_addr);
- rc = connect(fd, (struct sockaddr *) &sin6, sizeof(sin6));
- break;
- default:
- return -1;
- }
-
- return rc;
-}
-
+/**
+ * Virtual UM interface file descriptor callback.
+ * Should be called by select.c when the fd is ready for reading.
+ */
static int virt_um_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
struct virt_um_inst *vui = ofd->data;
+ // check if the read flag is set
if (what & BSC_FD_READ) {
- struct msgb *msg = msgb_alloc(VIRT_UM_MSGB_SIZE, "Virtual UM Rx");
+ // allocate message buffer of specified size
+ struct msgb *msg = msgb_alloc(VIRT_UM_MSGB_SIZE,
+ "Virtual UM Rx");
int rc;
- rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg));
+ // read message from fd in message buffer
+ rc = mcast_bidir_sock_rx(vui->mcast_sock, msgb_data(msg),
+ msgb_tailroom(msg));
+ // rc is number of bytes actually read
if (rc > 0) {
msgb_put(msg, rc);
+ msg->l1h = msgb_data(msg);
+ // call the l1 callback function for a received msg
vui->recv_cb(vui, msg);
} else {
+ // TODO: this kind of error handling might be a bit harsh
vui->recv_cb(vui, NULL);
+ // Unregister fd from select loop
osmo_fd_unregister(ofd);
close(ofd->fd);
ofd->fd = -1;
@@ -141,70 +67,36 @@ static int virt_um_fd_cb(struct osmo_fd *ofd, unsigned int what)
return 0;
}
-struct virt_um_inst *virt_um_init(void *ctx, const char *group, uint16_t port,
- const char *netdev, void *priv,
- void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg))
+struct virt_um_inst *virt_um_init(
+ void *ctx, const char *tx_mcast_group, uint16_t tx_mcast_port,
+ const char *rx_mcast_group, uint16_t rx_mcast_port,
+ void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg))
{
- struct virt_um_inst *vui;
- int fd, rc;
-
- if (!port)
- port = GSMTAP_UDP_PORT;
- if (!group)
- group = "239.0.47.29";
-
- /* crate a socked and bind it to the multicast group. Do NOT
- * specify a fixed port locally, to make stack choose a random
- * free UDP port. */
- fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, group,
- 0, OSMO_SOCK_F_BIND);
- if (fd < 0)
- return NULL;
-
- /* join the multicast group */
- rc = mcast_join_group(fd, group, netdev);
- if (rc < 0) {
- close(fd);
- return NULL;
- }
-
- /* and finally also connect */
- rc = mcast_connect(fd, group, port);
- if (rc < 0) {
- close(fd);
- return NULL;
- }
-
- vui = talloc_zero(ctx, struct virt_um_inst);
- vui->priv = priv;
+ struct virt_um_inst *vui = talloc_zero(ctx, struct virt_um_inst);
+ vui->mcast_sock = mcast_bidir_sock_setup(ctx, tx_mcast_group,
+ tx_mcast_port, rx_mcast_group, rx_mcast_port, 1,
+ virt_um_fd_cb, vui);
vui->recv_cb = recv_cb;
- vui->ofd.data = vui;
- vui->ofd.fd = fd;
- vui->ofd.when = BSC_FD_READ;
- vui->ofd.cb = virt_um_fd_cb;
-
- osmo_fd_register(&vui->ofd);
return vui;
+
}
void virt_um_destroy(struct virt_um_inst *vui)
{
- struct osmo_fd *ofd = &vui->ofd;
-
- osmo_fd_unregister(ofd);
- close(ofd->fd);
- ofd->fd = -1;
- ofd->when = 0;
-
+ mcast_bidir_sock_close(vui->mcast_sock);
talloc_free(vui);
}
+/**
+ * Write msg to to multicast socket and free msg afterwards
+ */
int virt_um_write_msg(struct virt_um_inst *vui, struct msgb *msg)
{
int rc;
- rc = write(vui->ofd.fd, msgb_data(msg), msgb_length(msg));
+ rc = mcast_bidir_sock_tx(vui->mcast_sock, msgb_data(msg),
+ msgb_length(msg));
msgb_free(msg);
return rc;
diff --git a/src/osmo-bts-virtual/virtual_um.h b/src/osmo-bts-virtual/virtual_um.h
index 65292211..6b782af4 100644
--- a/src/osmo-bts-virtual/virtual_um.h
+++ b/src/osmo-bts-virtual/virtual_um.h
@@ -2,16 +2,24 @@
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
+#include "osmo_mcast_sock.h"
+
+#define VIRT_UM_MSGB_SIZE 256
+#define DEFAULT_MS_MCAST_GROUP "224.0.0.1"
+#define DEFAULT_MS_MCAST_PORT 6666
+#define DEFAULT_BTS_MCAST_GROUP "225.0.0.1"
+#define DEFAULT_BTS_MCAST_PORT 6667
struct virt_um_inst {
void *priv;
- struct osmo_fd ofd;
+ struct mcast_bidir_sock *mcast_sock;
void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg);
};
-struct virt_um_inst *virt_um_init(void *ctx, const char *group, uint16_t port,
- const char *netdev, void *priv,
- void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg));
+struct virt_um_inst *virt_um_init(
+ void *ctx, const char *tx_mcast_group, uint16_t tx_mcast_port,
+ const char *rx_mcast_group, uint16_t rx_mcast_port,
+ void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg));
void virt_um_destroy(struct virt_um_inst *vui);
diff --git a/src/osmo-bts-virtual/virtualbts_vty.c b/src/osmo-bts-virtual/virtualbts_vty.c
index e8c97acc..83a32f67 100644
--- a/src/osmo-bts-virtual/virtualbts_vty.c
+++ b/src/osmo-bts-virtual/virtualbts_vty.c
@@ -63,63 +63,105 @@ void bts_model_config_write_phy(struct vty *vty, struct phy_link *plink)
{
if (plink->u.virt.mcast_dev)
vty_out(vty, " virtual-um net-device %s%s",
- plink->u.virt.mcast_dev, VTY_NEWLINE);
- if (plink->u.virt.mcast_group)
- vty_out(vty, " virtual-um multicast-group %s%s",
- plink->u.virt.mcast_group, VTY_NEWLINE);
- if (plink->u.virt.mcast_port)
- vty_out(vty, " virtual-um udp-port %u%s",
- plink->u.virt.mcast_port, VTY_NEWLINE);
+ plink->u.virt.mcast_dev, VTY_NEWLINE);
+ if (plink->u.virt.ms_mcast_group)
+ vty_out(vty, " virtual-um ms-multicast-group %s%s",
+ plink->u.virt.ms_mcast_group, VTY_NEWLINE);
+ if (plink->u.virt.ms_mcast_port)
+ vty_out(vty, " virtual-um ms-udp-port %u%s",
+ plink->u.virt.ms_mcast_port, VTY_NEWLINE);
+ if (plink->u.virt.bts_mcast_group)
+ vty_out(vty, " virtual-um bts-multicast-group %s%s",
+ plink->u.virt.bts_mcast_group, VTY_NEWLINE);
+ if (plink->u.virt.bts_mcast_port)
+ vty_out(vty, " virtual-um bts-udp-port %u%s",
+ plink->u.virt.bts_mcast_port, VTY_NEWLINE);
+
}
#define VUM_STR "Virtual Um layer\n"
-DEFUN(cfg_phy_mcast_group, cfg_phy_mcast_group_cmd,
- "virtual-um multicast-group GROUP",
- VUM_STR "Configure the multicast group\n")
+DEFUN(cfg_phy_ms_mcast_group, cfg_phy_ms_mcast_group_cmd,
+ "virtual-um ms-multicast-group GROUP",
+ VUM_STR "Configure the MS multicast group\n")
+{
+ struct phy_link *plink = vty->index;
+
+ if (plink->state != PHY_LINK_SHUTDOWN) {
+ vty_out(vty, "Can only reconfigure a PHY link that is down%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (plink->u.virt.ms_mcast_group)
+ talloc_free(plink->u.virt.ms_mcast_group);
+ plink->u.virt.ms_mcast_group = talloc_strdup(plink, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_phy_ms_mcast_port, cfg_phy_ms_mcast_port_cmd,
+ "virtual-um ms-udp-port <0-65535>",
+ VUM_STR "Configure the MS UDP port\n")
{
struct phy_link *plink = vty->index;
if (plink->state != PHY_LINK_SHUTDOWN) {
vty_out(vty, "Can only reconfigure a PHY link that is down%s",
- VTY_NEWLINE);
+ VTY_NEWLINE);
return CMD_WARNING;
}
- if (plink->u.virt.mcast_group)
- talloc_free(plink->u.virt.mcast_group);
- plink->u.virt.mcast_group = talloc_strdup(plink, argv[0]);
+ plink->u.virt.ms_mcast_port = atoi(argv[0]);
return CMD_SUCCESS;
}
+DEFUN(cfg_phy_bts_mcast_group, cfg_phy_bts_mcast_group_cmd,
+ "virtual-um bts-multicast-group GROUP",
+ VUM_STR "Configure the BTS multicast group\n")
+{
+ struct phy_link *plink = vty->index;
+
+ if (plink->state != PHY_LINK_SHUTDOWN) {
+ vty_out(vty, "Can only reconfigure a PHY link that is down%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (plink->u.virt.bts_mcast_group)
+ talloc_free(plink->u.virt.bts_mcast_group);
+ plink->u.virt.bts_mcast_group = talloc_strdup(plink, argv[0]);
+
+ return CMD_SUCCESS;
+}
-DEFUN(cfg_phy_mcast_port, cfg_phy_mcast_port_cmd,
- "virtual-um udp-port <0-65535>",
- VUM_STR "Configure the UDP port\n")
+DEFUN(cfg_phy_bts_mcast_port, cfg_phy_bts_mcast_port_cmd,
+ "virtual-um bts-udp-port <0-65535>",
+ VUM_STR "Configure the BTS UDP port\n")
{
struct phy_link *plink = vty->index;
if (plink->state != PHY_LINK_SHUTDOWN) {
vty_out(vty, "Can only reconfigure a PHY link that is down%s",
- VTY_NEWLINE);
+ VTY_NEWLINE);
return CMD_WARNING;
}
- plink->u.virt.mcast_port = atoi(argv[0]);
+ plink->u.virt.bts_mcast_port = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_phy_mcast_dev, cfg_phy_mcast_dev_cmd,
- "virtual-um net-device NETDEV",
- VUM_STR "Configure the network device\n")
+ "virtual-um net-device NETDEV",
+ VUM_STR "Configure the network device\n")
{
struct phy_link *plink = vty->index;
if (plink->state != PHY_LINK_SHUTDOWN) {
vty_out(vty, "Can only reconfigure a PHY link that is down%s",
- VTY_NEWLINE);
+ VTY_NEWLINE);
return CMD_WARNING;
}
@@ -134,8 +176,10 @@ int bts_model_vty_init(struct gsm_bts *bts)
{
vty_bts = bts;
- install_element(PHY_NODE, &cfg_phy_mcast_group_cmd);
- install_element(PHY_NODE, &cfg_phy_mcast_port_cmd);
+ install_element(PHY_NODE, &cfg_phy_ms_mcast_group_cmd);
+ install_element(PHY_NODE, &cfg_phy_ms_mcast_port_cmd);
+ install_element(PHY_NODE, &cfg_phy_bts_mcast_group_cmd);
+ install_element(PHY_NODE, &cfg_phy_bts_mcast_port_cmd);
install_element(PHY_NODE, &cfg_phy_mcast_dev_cmd);
return 0;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 54ff0297..cf2e2b39 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = paging cipher agch misc bursts handover
+SUBDIRS = paging cipher agch misc bursts handover virtsock
if ENABLE_SYSMOBTS
SUBDIRS += sysmobts
diff --git a/tests/virtsock/Makefile.am b/tests/virtsock/Makefile.am
new file mode 100644
index 00000000..29c7442f
--- /dev/null
+++ b/tests/virtsock/Makefile.am
@@ -0,0 +1,15 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
+AM_CFLAGS = -D_GNU_SOURCE -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(ORTP_CFLAGS)
+LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(ORTP_LIBS)
+noinst_PROGRAMS = client_bidir client_mcast client_unix server_bidir server_mcast server_unix virt_um_bts virt_um_ms
+# EXTRA_DIST = client_bidir.ok client_mcast.ok client_unix.ok server_bidir.ok server_mcast.ok server_unix.ok virt_um_bts.ok virt_um_bts.ok
+
+client_bidir_SOURCES = virtsock_client_mcast_bidir.c mcast_sock.c
+#client_bidir_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
+server_bidir_SOURCES = virtsock_server_mcast_bidir.c mcast_sock.c
+client_mcast_SOURCES = virtsock_client_mcast.c
+server_mcast_SOURCES = virtsock_server_mcast.c
+client_unix_SOURCES = virtsock_client_unix_domain.c
+server_unix_SOURCES = virtsock_server_unix_domain.c
+virt_um_ms_SOURCES = virt_um_ms.c $(top_srcdir)/src/osmo-bts-virtual/virtual_um.c $(top_srcdir)/src/osmo-bts-virtual/osmo_mcast_sock.c
+virt_um_bts_SOURCES = virt_um_bts.c $(top_srcdir)/src/osmo-bts-virtual/virtual_um.c $(top_srcdir)/src/osmo-bts-virtual/osmo_mcast_sock.c
diff --git a/tests/virtsock/mcast_sock.c b/tests/virtsock/mcast_sock.c
new file mode 100644
index 00000000..4ca562a0
--- /dev/null
+++ b/tests/virtsock/mcast_sock.c
@@ -0,0 +1,220 @@
+#include <netinet/in.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <bits/sockaddr.h>
+
+#include "mcast_sock.h"
+
+struct mcast_server_sock *mcast_server_sock_setup(char* tx_mcast_group,
+ int tx_mcast_port,
+ int loopback)
+{
+ struct mcast_server_sock *serv_sock = malloc(
+ sizeof(struct mcast_server_sock));
+ struct sockaddr_in *tx_mcast_sock_conf = malloc(
+ sizeof(struct sockaddr_in));
+
+ // setup mcast server socket
+ serv_sock->sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (serv_sock->sd == -1) {
+ perror("Failed to create Multicast Socket.\n");
+ return NULL;
+ }
+ memset(tx_mcast_sock_conf, 0, sizeof(*tx_mcast_sock_conf));
+ tx_mcast_sock_conf->sin_family = AF_INET;
+ tx_mcast_sock_conf->sin_addr.s_addr = inet_addr(tx_mcast_group);
+ tx_mcast_sock_conf->sin_port = htons(tx_mcast_port);
+ serv_sock->sock_conf = tx_mcast_sock_conf;
+
+ // disable loopback
+ if (loopback) {
+ char loop = 0;
+ if (setsockopt(serv_sock->sd, IPPROTO_IP,
+ IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) {
+ perror("Failed to disable loopback.\n");
+ return NULL;
+ }
+ }
+
+// // setup rx unix domain socket
+// serv_sock->sd = socket(AF_LOCAL, SOCK_STREAM, IPPROTO_UDP);
+// if (serv_sock->sd < 0) {
+// perror("Failed to create Unix Domain Socket.\n");
+// return NULL;
+// }
+// rx_sock_conf->sun_family = AF_LOCAL;
+// strcpy(rx_sock_conf->sun_path, rx_unix_path);
+// unlink(rx_sock_conf->sun_path);
+//
+// if (bind(serv_sock->sd, (struct sockaddr *)&rx_sock_conf,
+// sizeof(rx_sock_conf)) != 0) {
+// perror("Failed to bind the unix domain socket. '%s'\n",
+// rx_sock_conf->sun_path);
+// return NULL;
+// }
+//
+// if (listen(serv_sock->sd, 0) != 0) {
+// perror("Failed to listen.\n");
+// return NULL;
+// }
+ return serv_sock;
+}
+
+struct mcast_client_sock *mcast_client_sock_setup(char* mcast_group,
+ int mcast_port)
+{
+ struct mcast_client_sock *client_sock = malloc(
+ sizeof(struct mcast_client_sock));
+ struct sockaddr_in *rx_sock_conf = malloc(sizeof(struct sockaddr_in));
+
+ struct ip_mreq *group_conf = malloc(sizeof(struct ip_mreq));
+ int rc, opt = 1;
+
+ // setup mcast client socket
+ client_sock->sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (client_sock->sd == -1) {
+ return NULL;
+ }
+
+ memset(rx_sock_conf, 0, sizeof(*rx_sock_conf));
+ rx_sock_conf->sin_family = AF_INET;
+ // INADDR_ANY -> don't specify an interface, let OS choose the proper one
+ rx_sock_conf->sin_addr.s_addr = htonl(INADDR_ANY);
+ rx_sock_conf->sin_port = htons(mcast_port);
+ client_sock->sock_conf = rx_sock_conf;
+
+ /* let multiple processes use the same port */
+ rc = setsockopt(client_sock->sd,
+ SOL_SOCKET,
+ SO_REUSEADDR, &opt, sizeof(opt));
+ if (rc < 0) {
+ return NULL;
+ }
+ rc = bind(client_sock->sd, (struct sockaddr *)client_sock->sock_conf,
+ sizeof(*client_sock->sock_conf));
+ if (rc < 0) {
+ return NULL;
+ }
+
+ /* configure broadcast on this machine */
+ opt = 1;
+ rc = setsockopt(client_sock->sd,
+ IPPROTO_IP,
+ IP_MULTICAST_LOOP, &opt, sizeof(opt));
+ if (rc < 0) {
+ return NULL;
+ }
+
+ /* configure the broadcast group */
+ group_conf->imr_multiaddr.s_addr = inet_addr(mcast_group);
+ group_conf->imr_interface.s_addr = htonl(INADDR_ANY);
+ if (group_conf->imr_multiaddr.s_addr == -1) {
+ return NULL;
+ }
+ client_sock->mcast_group = group_conf;
+
+ /* join the broadcast group */
+ rc = setsockopt(client_sock->sd,
+ IPPROTO_IP,
+ IP_ADD_MEMBERSHIP, client_sock->mcast_group,
+ sizeof(*client_sock->mcast_group));
+ if (rc < 0) {
+ return NULL;
+ }
+
+// // setup tx unix domain socket
+// client_sock->tx_sd = socket(AF_LOCAL, SOCK_STREAM, IPPROTO_UDP);
+// if (client_sock->tx_sd < 0) {
+// perror("Failed to create Unix Domain Socket.\n");
+// return NULL;
+// }
+// tx_sock_conf.sun_family = AF_LOCAL;
+// strcpy(tx_sock_conf.sun_path, tx_unix_path);
+// unlink(tx_sock_conf.sun_path);
+//
+// if (bind(client_sock->tx_sd, (struct sockaddr *)&tx_sock_conf,
+// sizeof(tx_sock_conf)) != 0) {
+// perror("Failed to bind the unix domain socket. '%s'\n",
+// tx_sock_conf.sun_path);
+// return NULL;
+// }
+//
+// if ((client_sock->tx_sd, (struct sockaddr *)&tx_sock_conf, sizeof(tx_sock_conf))
+// != 0) {
+// perror("Failed to listen.\n");
+// return NULL;
+// }
+
+ return client_sock;
+}
+
+struct mcast_bidir_sock *mcast_bidir_sock_setup(char* tx_mcast_group,
+ int tx_mcast_port,
+ char* rx_mcast_group,
+ int rx_mcast_port)
+{
+ struct mcast_bidir_sock *bidir_sock = malloc(
+ sizeof(struct mcast_bidir_sock));
+ bidir_sock->rx_sock = mcast_client_sock_setup(rx_mcast_group,
+ rx_mcast_port);
+ bidir_sock->tx_sock = mcast_server_sock_setup(tx_mcast_group,
+ tx_mcast_port, 0);
+ if (!bidir_sock->rx_sock || !bidir_sock->tx_sock) {
+ return NULL;
+ }
+ return bidir_sock;
+
+}
+
+int mcast_client_sock_rx(struct mcast_client_sock *client_sock, void* buf,
+ int buf_len)
+{
+ return recv(client_sock->sd, buf, buf_len, 0);
+}
+
+int mcast_server_sock_tx(struct mcast_server_sock *serv_sock, void* data,
+ int data_len)
+{
+ return sendto(serv_sock->sd, data, data_len, 0,
+ (struct sockaddr *)serv_sock->sock_conf,
+ sizeof(*serv_sock->sock_conf));
+}
+
+int mcast_bidir_sock_tx(struct mcast_bidir_sock *bidir_sock, void* data,
+ int data_len)
+{
+ return mcast_server_sock_tx(bidir_sock->tx_sock, data, data_len);
+}
+int mcast_bidir_sock_rx(struct mcast_bidir_sock *bidir_sock, void* buf,
+ int buf_len)
+{
+ return mcast_client_sock_rx(bidir_sock->rx_sock, buf, buf_len);
+}
+
+int mcast_client_sock_close(struct mcast_client_sock *client_sock)
+{
+ free(client_sock->mcast_group);
+ free(client_sock->sock_conf);
+ free(client_sock);
+ return setsockopt(client_sock->sd,
+ IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, client_sock->mcast_group,
+ sizeof(*client_sock->mcast_group))
+ + close(client_sock->sd);
+
+}
+int mcast_server_sock_close(struct mcast_server_sock *serv_sock)
+{
+ free(serv_sock->sock_conf);
+ free(serv_sock);
+ return close(serv_sock->sd) + close(serv_sock->sd);
+}
+
+int mcast_bidir_sock_close(struct mcast_bidir_sock *bidir_sock)
+{
+ free(bidir_sock);
+ return mcast_client_sock_close(bidir_sock->rx_sock)
+ + mcast_server_sock_close(bidir_sock->tx_sock);
+
+}
diff --git a/tests/virtsock/mcast_sock.h b/tests/virtsock/mcast_sock.h
new file mode 100644
index 00000000..ce1cbe70
--- /dev/null
+++ b/tests/virtsock/mcast_sock.h
@@ -0,0 +1,40 @@
+#include <netinet/in.h>
+
+struct mcast_server_sock {
+ int sd;
+ struct sockaddr_in *sock_conf;
+};
+
+struct mcast_client_sock {
+ int sd;
+ struct sockaddr_in *sock_conf;
+ struct ip_mreq *mcast_group;
+};
+
+struct mcast_bidir_sock {
+ struct mcast_server_sock *tx_sock;
+ struct mcast_client_sock *rx_sock;
+};
+
+struct mcast_bidir_sock *mcast_bidir_sock_setup(char* tx_mcast_group,
+ int tx_mcast_port,
+ char* rx_mcast_group,
+ int rx_mcast_port);
+
+struct mcast_server_sock *mcast_server_sock_setup(char* tx_mcast_group,
+ int tx_mcast_port,
+ int loopback);
+struct mcast_client_sock *mcast_client_sock_setup(char* mcast_group,
+ int mcast_port);
+int mcast_client_sock_rx(struct mcast_client_sock *client_sock, void* buf,
+ int buf_len);
+int mcast_server_sock_tx(struct mcast_server_sock *serv_sock, void* data,
+ int data_len);
+int mcast_bidir_sock_tx(struct mcast_bidir_sock *bidir_sock, void* data,
+ int data_len);
+int mcast_bidir_sock_rx(struct mcast_bidir_sock *bidir_sock, void* buf,
+ int buf_len);
+int mcast_client_sock_close(struct mcast_client_sock* client_sock);
+int mcast_server_sock_close(struct mcast_server_sock* server_sock);
+int mcast_bidir_sock_close(struct mcast_bidir_sock* bidir_sock);
+
diff --git a/tests/virtsock/virt_um_bts.c b/tests/virtsock/virt_um_bts.c
new file mode 100644
index 00000000..37807b70
--- /dev/null
+++ b/tests/virtsock/virt_um_bts.c
@@ -0,0 +1,61 @@
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <string.h>
+#include <talloc.h>
+#include <unistd.h>
+
+#include "../../src/osmo-bts-virtual/virtual_um.h"
+
+static char *rx_mcast_group = "224.0.0.1";
+static int rx_mcast_port = 6666;
+static char *tx_mcast_group = "225.0.0.1";
+static int tx_mcast_port = 6667;
+
+static void virt_um_rx_cb(struct virt_um_inst *vui, struct msgb *msg)
+{
+ char addrbuf[32];
+ inet_ntop(AF_INET, &vui->mcast_sock->rx_sock->mcast_group->imr_multiaddr, addrbuf, 32);
+ printf("Received ACK from MS(%s): \"%s\"\n", addrbuf, msgb_data(msg));
+}
+
+// main prog for a client program continuously receiving data from a multicast socket and responding to them answers on another
+int main(void)
+{
+ int i = 0;
+ struct virt_um_inst *vui = virt_um_init(NULL, tx_mcast_group,
+ tx_mcast_port, rx_mcast_group, rx_mcast_port,
+ virt_um_rx_cb);
+ if (!vui) {
+ perror("Error initializing vui.");
+ }
+
+ while (++i < 1000) {
+ struct msgb *outmsg = msgb_alloc(VIRT_UM_MSGB_SIZE,
+ "Virtual UM Rx");
+ char strbuf[64], addrbuf[32], *data_loc;
+ int port, rc;
+ sprintf(strbuf, "MSG: (%u)", i);
+ data_loc = (char *)msgb_put(outmsg, strlen(strbuf)+1);
+ memcpy(data_loc, strbuf, strlen(strbuf)+1);
+
+ rc = virt_um_write_msg(vui, outmsg);
+ inet_ntop(AF_INET, &vui->mcast_sock->tx_sock->sock_conf->sin_addr, addrbuf, 32);
+ port = vui->mcast_sock->tx_sock->sock_conf->sin_port;
+ if (rc < 0) {
+ perror("Error sending MSG");
+ } else if (rc == 0) {
+ printf("Nothing sent: \"%s\"\n", msgb_data(outmsg));
+ } else {
+ printf("Sent MSG to MS(%s:%d): \"%s\"\n", addrbuf, port, msgb_data(outmsg));
+ }
+ // receive all pending acks
+ while(osmo_select_main(1));
+
+ sleep(1);
+ }
+
+ virt_um_destroy(vui);
+ return 0;
+}
diff --git a/tests/virtsock/virt_um_ms.c b/tests/virtsock/virt_um_ms.c
new file mode 100644
index 00000000..ce061888
--- /dev/null
+++ b/tests/virtsock/virt_um_ms.c
@@ -0,0 +1,72 @@
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../../src/osmo-bts-virtual/virtual_um.h"
+
+static char *tx_mcast_group = "224.0.0.1";
+static int tx_mcast_port = 6666;
+static char *rx_mcast_group = "225.0.0.1";
+static int rx_mcast_port = 6667;
+
+void virt_um_rx_cb(struct virt_um_inst *vui, struct msgb *msg)
+{
+ struct msgb *outmsg = msgb_alloc(VIRT_UM_MSGB_SIZE, "Virtual UM Rx");
+ char *data_loc, strbuf[VIRT_UM_MSGB_SIZE], addrbuf[32];
+ int rc, port;
+
+ inet_ntop(AF_INET, &vui->mcast_sock->rx_sock->mcast_group->imr_multiaddr, addrbuf, 32);
+
+ printf("Received MSG from BTS(%s): \"%s\"\n", addrbuf, msgb_data(msg));
+
+ sprintf(strbuf, "ACK: (%s)", msgb_data(msg));
+ data_loc = (char *)msgb_put(outmsg, strlen(strbuf)+1);
+ memcpy(data_loc, strbuf, strlen(strbuf)+1);
+
+ inet_ntop(AF_INET, &vui->mcast_sock->tx_sock->sock_conf->sin_addr, addrbuf, 32);
+ port = vui->mcast_sock->tx_sock->sock_conf->sin_port;
+ rc = virt_um_write_msg(vui, outmsg);
+ if (rc < 0) {
+ perror("Error sending ACK");
+ } else if (rc == 0) {
+ printf("Nothing sent: \"%s\"\n", msgb_data(outmsg));
+ } else {
+ printf("Sent ACK to BTS(%s:%d): \"%s\"\n", addrbuf, port, msgb_data(outmsg));
+ }
+}
+
+// main prog for a client program continuously receiving data from a multicast socket and responding to them answers on another
+int main(void)
+{
+ int i = 0;
+ struct virt_um_inst *vui = virt_um_init(NULL, tx_mcast_group,
+ tx_mcast_port, rx_mcast_group, rx_mcast_port,
+ virt_um_rx_cb);
+ if (!vui) {
+ perror("Error initializing vui.");
+ }
+ while (++i < 1000) {
+ printf("Waiting for MSG NR. %d\n", i);
+ osmo_select_main(0);
+ // fallback -> will the communication work without the fd callback?
+// {
+// char rx_buf[256], tx_buf[256];
+// while(mcast_bidir_sock_rx(vui->mcast_sock, rx_buf, sizeof(rx_buf)) < 0) {
+// perror("Error receiving message from server.");
+// }
+// printf("Received MSG from server: \"%s\"\n", rx_buf);
+//
+// strcpy(tx_buf,"ACK: \"");
+// strcat(tx_buf, rx_buf);
+// strcat(tx_buf, "\"");
+// if (mcast_bidir_sock_tx(vui->mcast_sock, tx_buf, sizeof(tx_buf)) < 0) {
+// perror("Error transmitting message to server.");
+// }
+// printf("Sent ACK to server: \"%s\"\n", tx_buf);
+// }
+ }
+ virt_um_destroy(vui);
+ return 0;
+}
diff --git a/tests/virtsock/virtsock_client.c b/tests/virtsock/virtsock_client.c
new file mode 100644
index 00000000..ba0f50c8
--- /dev/null
+++ b/tests/virtsock/virtsock_client.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+void error(const char *msg)
+{
+ perror(msg);
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ int sockfd, portno, n;
+ struct sockaddr_in serv_addr;
+ struct hostent *server;
+
+ char buffer[256];
+ if (argc < 3) {
+ fprintf(stderr,"usage %s hostname port\n", argv[0]);
+ exit(0);
+ }
+ portno = atoi(argv[2]);
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0)
+ error("ERROR opening socket");
+ server = gethostbyname(argv[1]);
+ if (server == NULL) {
+ fprintf(stderr,"ERROR, no such host\n");
+ exit(0);
+ }
+ bzero((char *) &serv_addr, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ bcopy((char *)server->h_addr,
+ (char *)&serv_addr.sin_addr.s_addr,
+ server->h_length);
+ serv_addr.sin_port = htons(portno);
+ if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
+ error("ERROR connecting");
+ printf("Please enter the message: ");
+ bzero(buffer,256);
+ fgets(buffer,255,stdin);
+ n = write(sockfd,buffer,strlen(buffer));
+ if (n < 0)
+ error("ERROR writing to socket");
+ bzero(buffer,256);
+ n = read(sockfd,buffer,255);
+ if (n < 0)
+ error("ERROR reading from socket");
+ printf("%s\n",buffer);
+ close(sockfd);
+ return 0;
+}
diff --git a/tests/virtsock/virtsock_client_mcast.c b/tests/virtsock/virtsock_client_mcast.c
new file mode 100644
index 00000000..4072f6df
--- /dev/null
+++ b/tests/virtsock/virtsock_client_mcast.c
@@ -0,0 +1,115 @@
+/* client.c */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+/* Adresse für multicast IP */
+static char *host_name = "224.0.0.1";
+static int port = 6666;
+static struct ip_mreq command;
+
+static int setup_multicast_socket(void)
+{
+ int loop = 1;
+ int socket_descriptor;
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = htons(port);
+ if ((socket_descriptor = socket(AF_INET,
+ SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+ perror("socket()");
+ exit(EXIT_FAILURE);
+ }
+ /* Mehr Prozessen erlauben, denselben Port zu nutzen */
+ loop = 1;
+ if (setsockopt(socket_descriptor,
+ SOL_SOCKET,
+ SO_REUSEADDR, &loop, sizeof(loop)) < 0) {
+ perror("setsockopt:SO_REUSEADDR");
+ exit(EXIT_FAILURE);
+ }
+ if (bind(socket_descriptor, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ perror("bind");
+ exit(EXIT_FAILURE);
+ }
+ /* Broadcast auf dieser Maschine zulassen */
+ loop = 1;
+ if (setsockopt(socket_descriptor,
+ IPPROTO_IP,
+ IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) {
+ perror("setsockopt:IP_MULTICAST_LOOP");
+ exit(EXIT_FAILURE);
+ }
+ /* Join the broadcast group: */
+ command.imr_multiaddr.s_addr = inet_addr(host_name);
+ command.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (command.imr_multiaddr.s_addr == -1) {
+ perror("Keine Multicast-Adresse\n");
+ exit(EXIT_FAILURE);
+ }
+ if (setsockopt(socket_descriptor,
+ IPPROTO_IP,
+ IP_ADD_MEMBERSHIP, &command, sizeof(command)) < 0) {
+ perror("setsockopt:IP_ADD_MEMBERSHIP");
+ }
+ if(fcntl(socket_descriptor, F_SETFL, fcntl(socket_descriptor, F_GETFL) | O_NONBLOCK) < 0) {
+ perror("fcntl:SOCK_SET_NONBLOCK");
+ }
+
+
+// memset(&sin, 0, sizeof(sin));
+// sin.sin_family = AF_INET;
+// sin.sin_port = htons(57286);
+// inet_pton(AF_INET, "192.168.5.48", &sin.sin_addr);
+// if (connect(socket_descriptor, (struct sockaddr *) &sin, sizeof(sin)) !=0 ) {
+// perror ("connect()");
+// }
+
+ return socket_descriptor;
+}
+int main(void)
+{
+ int iter = 0;
+ int sin_len;
+ char message[256];
+ int socket;
+ struct sockaddr_in sin;
+ struct hostent *server_host_name;
+ if ((server_host_name = gethostbyname(host_name)) == 0) {
+ perror("gethostbyname");
+ exit(EXIT_FAILURE);
+ }
+ socket = setup_multicast_socket();
+ /* Broadcast-Nachrichten empfangen */
+ // while (iter++ <= 10) {
+ while (++iter) {
+ sin_len = sizeof(sin);
+ char addr[32];
+ int port;
+ while (!recvfrom(socket, message, 256, 0, (struct sockaddr *)&sin,
+ &sin_len)) {
+ }
+ inet_ntop(AF_INET, &sin.sin_addr, addr, 32);
+ port = ntohs(sin.sin_port);
+ printf("Message %d von %s:%d: %s\n", iter, addr, port, message);
+ sendto(socket, "Received your message", sizeof("Received your message"), 0, (struct sockaddr *)&sin,
+ sin_len);
+ }
+ /* Multicast-Socket aus der Gruppe entfernen */
+ if (setsockopt(socket,
+ IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, &command, sizeof(command)) < 0) {
+ perror("setsockopt:IP_DROP_MEMBERSHIP");
+ }
+ close(socket);
+ return EXIT_SUCCESS;
+}
diff --git a/tests/virtsock/virtsock_client_mcast_bidir.c b/tests/virtsock/virtsock_client_mcast_bidir.c
new file mode 100644
index 00000000..63d4bcdb
--- /dev/null
+++ b/tests/virtsock/virtsock_client_mcast_bidir.c
@@ -0,0 +1,51 @@
+/* client.c */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "mcast_sock.h"
+
+static char *tx_mcast_group = "224.0.0.1";
+static int tx_mcast_port = 6666;
+static char *rx_mcast_group = "225.0.0.1";
+static int rx_mcast_port = 6667;
+
+// main prog for a client program continuously receiving data from a multicast socket and responding to them answers on another
+int main(void)
+{
+ int i = 0;
+ char rx_buf[256], tx_buf[256];
+ struct mcast_bidir_sock *sock = mcast_bidir_sock_setup(tx_mcast_group,
+ tx_mcast_port, rx_mcast_group, rx_mcast_port);
+ if (!sock) {
+ perror("Error initializing bidirectional sock.");
+ }
+
+ while (++i) {
+ printf("Waiting for message NR. %d\n", i);
+ if (mcast_bidir_sock_rx(sock, rx_buf, sizeof(rx_buf)) < 0) {
+ perror("Error receiving message from server.");
+ }
+ printf("Received MSG from server: \"%s\"\n", rx_buf);
+
+ strcpy(tx_buf,"ACK: \"");
+ strcat(tx_buf, rx_buf);
+ strcat(tx_buf, "\"");
+ if (mcast_bidir_sock_tx(sock, tx_buf, sizeof(tx_buf)) < 0) {
+ perror("Error transmitting message to server.");
+ }
+ printf("Sent ACK to server: \"%s\"\n", tx_buf);
+ }
+ if(mcast_bidir_sock_close(sock)) {
+ perror("Error closing sockets.");
+ }
+
+}
diff --git a/tests/virtsock/virtsock_client_unix_domain.c b/tests/virtsock/virtsock_client_unix_domain.c
new file mode 100644
index 00000000..a22c271c
--- /dev/null
+++ b/tests/virtsock/virtsock_client_unix_domain.c
@@ -0,0 +1,31 @@
+/* uds_client.c */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#define BUF 1024
+#define UDS_FILE "/tmp/osmocom_l2"
+int main(int argc, char **argv) {
+ int create_socket;
+ char *buffer = malloc(BUF);
+ struct sockaddr_un address;
+ int size;
+ if ((create_socket = socket(PF_LOCAL, SOCK_STREAM, 0)) > 0)
+ printf("Socket wurde angelegt\n");
+ address.sun_family = AF_LOCAL;
+ strcpy(address.sun_path, UDS_FILE);
+ if (connect(create_socket, (struct sockaddr *) &address, sizeof(address))
+ == 0)
+ printf("Verbindung mit dem Server hergestellt\n");
+ while (strcmp(buffer, "quit\n") != 0) {
+ printf("Nachricht zum Versenden: ");
+ fgets(buffer, BUF, stdin);
+ send(create_socket, buffer, strlen(buffer), 0);
+ }
+ close(create_socket);
+ return EXIT_SUCCESS;
+}
diff --git a/tests/virtsock/virtsock_server.c b/tests/virtsock/virtsock_server.c
new file mode 100644
index 00000000..6602dd72
--- /dev/null
+++ b/tests/virtsock/virtsock_server.c
@@ -0,0 +1,55 @@
+/* A simple server in the internet domain using TCP
+ The port number is passed as an argument */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+void error(const char *msg)
+{
+ perror(msg);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int sockfd, newsockfd, portno;
+ socklen_t clilen;
+ char buffer[256];
+ struct sockaddr_in serv_addr, cli_addr;
+ int n;
+ if (argc < 2) {
+ fprintf(stderr,"ERROR, no port provided\n");
+ exit(1);
+ }
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0)
+ error("ERROR opening socket");
+ bzero((char *) &serv_addr, sizeof(serv_addr));
+ portno = atoi(argv[1]);
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = INADDR_ANY;
+ serv_addr.sin_port = htons(portno);
+ if (bind(sockfd, (struct sockaddr *) &serv_addr,
+ sizeof(serv_addr)) < 0)
+ error("ERROR on binding");
+ listen(sockfd,5);
+ clilen = sizeof(cli_addr);
+ newsockfd = accept(sockfd,
+ (struct sockaddr *) &cli_addr,
+ &clilen);
+ if (newsockfd < 0)
+ error("ERROR on accept");
+ bzero(buffer,256);
+ n = read(newsockfd,buffer,255);
+ if (n < 0) error("ERROR reading from socket");
+ printf("Here is the message: %s\n",buffer);
+ n = write(newsockfd,"I got your message",18);
+ if (n < 0) error("ERROR writing to socket");
+ close(newsockfd);
+ close(sockfd);
+ return 0;
+}
diff --git a/tests/virtsock/virtsock_server_mcast.c b/tests/virtsock/virtsock_server_mcast.c
new file mode 100644
index 00000000..13b15e7c
--- /dev/null
+++ b/tests/virtsock/virtsock_server_mcast.c
@@ -0,0 +1,48 @@
+/* server.c */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+static int port = 6666;
+
+int main(void)
+{
+ int socket_descriptor;
+ struct sockaddr_in address;
+ socket_descriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (socket_descriptor == -1) {
+ perror("socket()");
+ exit(EXIT_FAILURE);
+ }
+ memset(&address, 0, sizeof(address));
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = inet_addr("224.0.0.1");
+ address.sin_port = htons(port);
+
+ if(fcntl(socket_descriptor, F_SETFL, fcntl(socket_descriptor, F_GETFL) | O_NONBLOCK) < 0) {
+ perror("fcntl:SOCK_SET_NONBLOCK");
+ }
+
+ printf("Server ist bereit ...\n");
+ /* Broadcasting beginnen */
+ while (1) {
+ char message[256];
+ if (sendto(socket_descriptor, "broadcast test (hallo client)",
+ sizeof("broadcast test (hallo client)"), 0,
+ (struct sockaddr *)&address, sizeof(address))
+ < 0) {
+ perror("sendto()");
+ exit(EXIT_FAILURE);
+ }
+ recv(socket_descriptor, message, 256, 0);
+ sleep(1);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/tests/virtsock/virtsock_server_mcast_bidir.c b/tests/virtsock/virtsock_server_mcast_bidir.c
new file mode 100644
index 00000000..b3366315
--- /dev/null
+++ b/tests/virtsock/virtsock_server_mcast_bidir.c
@@ -0,0 +1,54 @@
+/* client.c */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <strings.h>
+
+#include "mcast_sock.h"
+
+static char *rx_mcast_group = "224.0.0.1";
+static int rx_mcast_port = 6666;
+static char *tx_mcast_group = "225.0.0.1";
+static int tx_mcast_port = 6667;
+
+// main prog for a server program sending continuously some data to a multicast socket and receiving answers on another
+int main(void)
+{
+ int i = 0;
+ char rx_buf[256], tx_buf[256];
+ struct mcast_bidir_sock *sock = mcast_bidir_sock_setup(tx_mcast_group, tx_mcast_port, rx_mcast_group, rx_mcast_port);
+ if (!sock) {
+ perror("Error initializing bidirectional sock");
+ }
+
+ while (++i) {
+ char *ibuf;
+ strcpy(tx_buf, "MSG NR.");
+ asprintf(&ibuf, "%d", i);
+ strcat(tx_buf, ibuf);
+ if (mcast_bidir_sock_tx(sock, tx_buf, sizeof(tx_buf))
+ < 0) {
+ perror("Error transmitting message to server");
+ }
+ printf("Sent MSG to client: \"%s\"\n", tx_buf);
+ if (mcast_bidir_sock_rx(sock, rx_buf, sizeof(rx_buf))
+ < 0) {
+ perror("Error receiving message from server");
+ }
+ printf("Received ACK from client: \"%s\"\n", rx_buf);
+
+ sleep(1);
+ }
+ if(mcast_bidir_sock_close(sock)) {
+ perror("Error closing sockets.");
+ }
+
+}
diff --git a/tests/virtsock/virtsock_server_unix_domain.c b/tests/virtsock/virtsock_server_unix_domain.c
new file mode 100644
index 00000000..956c3592
--- /dev/null
+++ b/tests/virtsock/virtsock_server_unix_domain.c
@@ -0,0 +1,46 @@
+/* uds_server.c */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#define BUF 1024
+#define UDS_FILE "/tmp/osmocom_l2"
+int main(void) {
+ int create_socket, new_socket;
+ socklen_t addrlen;
+ char *buffer = malloc(BUF);
+ ssize_t size;
+ struct sockaddr_un address;
+ const int y = 1;
+ printf("\e[2J");
+ if ((create_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) > 0)
+ printf("Socket wurde angelegt\n");
+ unlink(UDS_FILE);
+ address.sun_family = AF_LOCAL;
+ strcpy(address.sun_path, UDS_FILE);
+ if (bind(create_socket, (struct sockaddr *) &address, sizeof(address))
+ != 0) {
+ printf("Der Port ist nicht frei – belegt!\n");
+ }
+ listen(create_socket, 5);
+ addrlen = sizeof(struct sockaddr_in);
+ new_socket = accept(create_socket, (struct sockaddr *) &address, &addrlen);
+ if (new_socket > 0)
+ printf("Ein Client ist verbunden ...\n");
+ do {
+ printf("Nachricht zum Versenden: ");
+ fgets(buffer, BUF, stdin);
+ send(new_socket, buffer, strlen(buffer), 0);
+ size = recv(new_socket, buffer, BUF - 1, 0);
+ if (size > 0)
+ buffer[size] = '\0';
+ printf("Nachricht empfangen: %s\n", buffer);
+ } while (strcmp(buffer, "quit\n") != 0);
+ close(new_socket);
+ close(create_socket);
+ return EXIT_SUCCESS;
+}