diff options
28 files changed, 1640 insertions, 391 deletions
@@ -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, ×lot); + 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; +} |