diff --git a/include/osmocom/bsc/abis_rsl.h b/include/osmocom/bsc/abis_rsl.h index 2611a3d00..9879497f4 100644 --- a/include/osmocom/bsc/abis_rsl.h +++ b/include/osmocom/bsc/abis_rsl.h @@ -118,5 +118,7 @@ int ipacc_payload_type(enum gsm48_chan_mode tch_mode, enum gsm_chan_t type); int rsl_tx_rf_chan_release(struct gsm_lchan *lchan); +void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts); + #endif /* RSL_MT_H */ diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h index a50c70e64..7f369042e 100644 --- a/include/osmocom/bsc/bts.h +++ b/include/osmocom/bsc/bts.h @@ -634,6 +634,7 @@ struct gsm_bts { struct osmo_timer_list etws_timer; /* when to stop ETWS PN */ struct llist_head oml_fail_rep; + struct llist_head chan_rqd_queue; }; #define GSM_BTS_SI2Q(bts, i) (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i]) diff --git a/include/osmocom/bsc/lchan_fsm.h b/include/osmocom/bsc/lchan_fsm.h index df3ed2212..9fe7db107 100644 --- a/include/osmocom/bsc/lchan_fsm.h +++ b/include/osmocom/bsc/lchan_fsm.h @@ -74,3 +74,5 @@ bool lchan_may_receive_data(struct gsm_lchan *lchan); void lchan_forget_conn(struct gsm_lchan *lchan); void lchan_set_last_error(struct gsm_lchan *lchan, const char *fmt, ...); + +void lchan_fsm_skip_error(struct gsm_lchan *lchan); diff --git a/include/osmocom/bsc/lchan_select.h b/include/osmocom/bsc/lchan_select.h index 865181bf5..41e7015cf 100644 --- a/include/osmocom/bsc/lchan_select.h +++ b/include/osmocom/bsc/lchan_select.h @@ -4,3 +4,4 @@ struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type); struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts, enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate); +struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type); diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c index 03d53c5bf..74cc760c5 100644 --- a/src/osmo-bsc/abis_rsl.c +++ b/src/osmo-bsc/abis_rsl.c @@ -1330,77 +1330,258 @@ int rsl_tx_imm_ass_rej(struct gsm_bts *bts, struct gsm48_req_ref *rqd_ref) return rsl_send_imm_ass_rej(bts, rqd_ref, wait_ind); } +struct chan_rqd { + struct llist_head entry; + struct gsm_bts *bts; + struct gsm48_req_ref ref; + enum gsm_chreq_reason_t reason; + uint8_t ta; + /* set to true to mark that the release of the release_lchan is in progress */ + struct gsm_lchan *release_lchan; + time_t timestamp; +}; + /* Handle packet channel rach requests */ -static int rsl_rx_pchan_rqd(struct msgb *msg, struct gsm_bts *bts) +static int rsl_rx_pchan_rqd(struct chan_rqd *rqd) { - struct gsm48_req_ref *rqd_ref; - struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); - rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; - uint8_t ra = rqd_ref->ra; uint8_t t1, t2, t3; uint32_t fn; uint8_t rqd_ta; uint8_t is_11bit; /* Process rach request and forward contained information to PCU */ - if (ra == 0x7F) { + if (rqd->ref.ra == 0x7F) { is_11bit = 1; /* FIXME: Also handle 11 bit rach requests */ - LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",bts->nr); + LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",rqd->bts->nr); return -EINVAL; } else { is_11bit = 0; - t1 = rqd_ref->t1; - t2 = rqd_ref->t2; - t3 = rqd_ref->t3_low | (rqd_ref->t3_high << 3); + t1 = rqd->ref.t1; + t2 = rqd->ref.t2; + t3 = rqd->ref.t3_low | (rqd->ref.t3_high << 3); fn = (51 * ((t3-t2) % 26) + t3 + 51 * 26 * t1); - - rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; + rqd_ta = rqd->ta; } - return pcu_tx_rach_ind(bts, rqd_ta, ra, fn, is_11bit, + return pcu_tx_rach_ind(rqd->bts, rqd_ta, rqd->ref.ra, fn, is_11bit, GSM_L1_BURST_TYPE_ACCESS_0); } +/* Protect against RACH DoS attack: If an excessive amount of RACH requests queues up it is likely that the current BTS + * is under RACH DoS attack. To prevent excessive memory usage, remove all expired or at least one of the oldest channel + * requests from the queue to prevent the queue from growing indefinetly. */ +static void reduce_rach_dos(struct gsm_bts *bts) +{ + int rlt = gsm_bts_get_radio_link_timeout(bts); + time_t timestamp_current = time(NULL); + struct chan_rqd *rqd; + struct chan_rqd *rqd_tmp; + unsigned int rqd_count = 0; + + /* Drop all expired channel requests in the list */ + llist_for_each_entry_safe(rqd, rqd_tmp, &bts->chan_rqd_queue, entry) { + /* If the channel request is older than the radio link timeout we drop it. This also means that the + * queue is under its overflow limit again. */ + if (timestamp_current - rqd->timestamp > rlt) + llist_del(&rqd->entry); + else + rqd_count++; + } + + /* If we find more than 255 (256) unexpired channel requests in the queue it is very likely that there is a + * problem with RACH dos on this BTS. We drop the first entry in the list to clip the growth of the list. */ + if (rqd_count > 255) { + LOG_BTS(bts, DRSL, LOGL_INFO, "CHAN RQD: more than 255 queued RACH requests -- RACH DoS attack?\n"); + llist_del(&llist_first_entry(&bts->chan_rqd_queue, struct chan_rqd, entry)->entry); + } +} + /* MS has requested a channel on the RACH */ static int rsl_rx_chan_rqd(struct msgb *msg) { - struct lchan_activate_info info; struct e1inp_sign_link *sign_link = msg->dst; struct gsm_bts *bts = sign_link->trx->bts; struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); - struct gsm48_req_ref *rqd_ref; - enum gsm_chan_t lctype; - enum gsm_chreq_reason_t chreq_reason; - struct gsm_lchan *lchan; - uint8_t rqd_ta; + struct chan_rqd *rqd; + + reduce_rach_dos(bts); + + rqd = talloc_zero(bts, struct chan_rqd); + OSMO_ASSERT(rqd); + + rqd->bts = bts; + rqd->timestamp = time(NULL); /* parse request reference to be used in immediate assign */ - if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) + if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) { + talloc_free(rqd); return -EINVAL; - - rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; + } + memcpy(&rqd->ref, &rqd_hdr->data[1], sizeof(rqd->ref)); /* parse access delay and use as TA */ - if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) + if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) { + talloc_free(rqd); return -EINVAL; - rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; + } + rqd->ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; /* Determine channel request cause code */ - chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); + rqd->reason = get_reason_by_chreq(rqd->ref.ra, bts->network->neci); LOG_BTS(bts, DRSL, LOGL_INFO, "CHAN RQD: reason: %s (ra=0x%02x, neci=0x%02x, chreq_reason=0x%02x)\n", - get_value_string(gsm_chreq_descs, chreq_reason), rqd_ref->ra, bts->network->neci, chreq_reason); + get_value_string(gsm_chreq_descs, rqd->reason), rqd->ref.ra, bts->network->neci, rqd->reason); - /* Handle PDCH related rach requests (in case of BSC-co-located-PCU */ - if (chreq_reason == GSM_CHREQ_REASON_PDCH) - return rsl_rx_pchan_rqd(msg, bts); + rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL]); + + /* Enqueue request */ + llist_add_tail(&rqd->entry, &bts->chan_rqd_queue); + + /* Forward the request directly. Most request will be finished with one attempt so no queuing will be + * necessary. */ + abis_rsl_chan_rqd_queue_poll(bts); + + return 0; +} + +/* Find any busy TCH/H or TCH/F lchan */ +static struct gsm_lchan *get_any_lchan(struct gsm_bts *bts) +{ + int trx_nr; + int ts_nr; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + struct gsm_lchan *lchan_est = NULL; + struct gsm_lchan *lchan_any = NULL; + struct gsm_lchan *lchan; + + for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { + trx = gsm_bts_trx_num(bts, trx_nr); + for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { + ts = &trx->ts[ts_nr]; + ts_for_each_lchan(lchan, ts) { + if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) { + if (bts->chan_alloc_reverse) { + if (lchan->fi->state == LCHAN_ST_ESTABLISHED) + lchan_est = lchan; + else + lchan_any = lchan; + } else { + if (lchan->fi->state == LCHAN_ST_ESTABLISHED) { + if (!lchan_est) + lchan_est = lchan; + } else { + if (!lchan_any) + lchan_any = lchan; + } + } + } + } + } + } + + if (lchan_est) + return lchan_est; + else if (lchan_any) + return lchan_any; + return NULL; +} + +/* Ensure that an incoming emergency call gets priority, if all voice channels are busy, terminate one regular call. + * Return true if freeing of a busy lchan is in progress, but not done yet, return false when done (either successfully + * or unsuccessfully). */ +static bool force_free_lchan_for_emergency(struct chan_rqd *rqd) +{ + /* If the request is not about an emergency call, we may exit early, without doing anything. */ + if (rqd->reason != GSM_CHREQ_REASON_EMERG) + return false; + + /* First check the situation on the BTS, if we have TCH/H or TCH/F resources available for another (EMERGENCY) + * call. If yes, then no (further) action has to be carried out. */ + if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_F)) { + LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE, + "CHAN RQD/EMERGENCY-PRIORITY: at least one TCH/F is (now) available!\n"); + return false; + } + if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_H)) { + LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE, + "CHAN RQD/EMERGENCY-PRIORITY: at least one TCH/H is (now) available!\n"); + return false; + } + + /* No free TCH/F or TCH/H was found, we now select one of the busy lchans and initate a release on that lchan. + * This will take a short amount of time. We need to come back and check regulary to see if we managed to + * free up another lchan. */ + if (!rqd->release_lchan) { + /* Pick any busy TCH/F or TCH/H lchan and inititate a channel + * release to make room for the incoming emergency call */ + rqd->release_lchan = get_any_lchan(rqd->bts); + if (!rqd->release_lchan) { + /* It can not happen that we first find out that there + * is no TCH/H or TCH/F available and at the same time + * we ware unable to find any busy TCH/H or TCH/F. In + * this case, the BTS probably does not have any + * voice channels configured? */ + LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE, + "CHAN RQD/EMERGENCY-PRIORITY: no TCH/H or TCH/F available - check VTY config!\n"); + return false; + } + + LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE, + "CHAN RQD/EMERGENCY-PRIORITY: inducing termination of lchan %s (state:%s) in favor of incoming EMERGENCY CALL!\n", + gsm_lchan_name(rqd->release_lchan), osmo_fsm_inst_state_name(rqd->release_lchan->fi)); + + lchan_release(rqd->release_lchan, !!(rqd->release_lchan->conn), true, 0); + } else { + /* BTS is shutting down, give up... */ + if (rqd->release_lchan->ts->fi->state == TS_ST_NOT_INITIALIZED) + return false; + + OSMO_ASSERT(rqd->release_lchan->fi); + + LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE, + "CHAN RQD/EMERGENCY-PRIORITY: still terminating lchan %s (state:%s) in favor of incoming EMERGENCY CALL!\n", + gsm_lchan_name(rqd->release_lchan), osmo_fsm_inst_state_name(rqd->release_lchan->fi)); + + /* If the channel was released in error (not established), the + * lchan FSM automatically blocks the LCHAN for a short time. + * This is not acceptable in an emergency situation, so we skip + * this waiting period. */ + if (rqd->release_lchan->fi->state == LCHAN_ST_WAIT_AFTER_ERROR) + lchan_fsm_skip_error(rqd->release_lchan); + } + + /* We are still in the process of releasing a busy lchan in favvor of the incoming emergency call. */ + return true; +} + +void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts) +{ + struct lchan_activate_info info; + enum gsm_chan_t lctype; + struct gsm_lchan *lchan = NULL; + struct chan_rqd *rqd; + + rqd = llist_first_entry_or_null(&bts->chan_rqd_queue, struct chan_rqd, entry); + if (!rqd) + return; + + /* Handle PDCH related rach requests (in case of BSC-co-located-PCU) */ + if (rqd->reason == GSM_CHREQ_REASON_PDCH) { + rsl_rx_pchan_rqd(rqd); + return; + } + + /* Ensure that emergency calls will get priority over regular calls, however releasing + * lchan in favor of an emergency call may take some time, so we exit here. The lchan_fsm + * will poll again when an lchan becomes available. */ + if (force_free_lchan_for_emergency(rqd)) + return; /* determine channel type (SDCCH/TCH_F/TCH_H) based on * request reference RA */ - lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); - - rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL]); + lctype = get_ctype_by_chreq(bts->network, rqd->ref.ra); /* check availability / allocate channel * @@ -1410,49 +1591,60 @@ static int rsl_rx_chan_rqd(struct msgb *msg) * - If there is still no channel available, try a TCH/F. * */ - if (chreq_reason == GSM_CHREQ_REASON_EMERG) { + if (rqd->reason == GSM_CHREQ_REASON_EMERG) { if (bts->si_common.rach_control.t2 & 0x4) { LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: MS attempts EMERGENCY CALL although EMERGENCY CALLS " "are not allowed in sysinfo (spec violation by MS!)\n"); - rsl_tx_imm_ass_rej(bts, rqd_ref); - return -EINVAL; + rsl_tx_imm_ass_rej(bts, &rqd->ref); + llist_del(&rqd->entry); + talloc_free(rqd); + return; } } - lchan = lchan_select_by_type(bts, GSM_LCHAN_SDCCH); + + /* Emergency calls will be put on a free TCH/H or TCH/F directly in the code below, all other channel requests + * will get an SDCCH first (if possible). */ + if (rqd->reason != GSM_CHREQ_REASON_EMERG) + lchan = lchan_select_by_type(bts, GSM_LCHAN_SDCCH); + if (!lchan) { LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: no resources for %s 0x%x, retrying with %s\n", - gsm_lchant_name(GSM_LCHAN_SDCCH), rqd_ref->ra, gsm_lchant_name(GSM_LCHAN_TCH_H)); + gsm_lchant_name(GSM_LCHAN_SDCCH), rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_H)); lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_H); } if (!lchan) { LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: no resources for %s 0x%x, retrying with %s\n", - gsm_lchant_name(GSM_LCHAN_SDCCH), rqd_ref->ra, gsm_lchant_name(GSM_LCHAN_TCH_F)); + gsm_lchant_name(GSM_LCHAN_SDCCH), rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_F)); lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_F); } if (!lchan) { LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: no resources for %s 0x%x\n", - gsm_lchant_name(lctype), rqd_ref->ra); + gsm_lchant_name(lctype), rqd->ref.ra); rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_NO_CHANNEL]); - rsl_tx_imm_ass_rej(bts, rqd_ref); - return 0; + rsl_tx_imm_ass_rej(bts, &rqd->ref); + llist_del(&rqd->entry); + talloc_free(rqd); + return; } /* save the RACH data as we need it after the CHAN ACT ACK */ lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref); OSMO_ASSERT(lchan->rqd_ref); - *(lchan->rqd_ref) = *rqd_ref; - lchan->rqd_ta = rqd_ta; + *(lchan->rqd_ref) = rqd->ref; + lchan->rqd_ta = rqd->ta; LOG_LCHAN(lchan, LOGL_DEBUG, "MS: Channel Request: reason=%s ra=0x%02x ta=%d\n", - gsm_chreq_name(chreq_reason), rqd_ref->ra, rqd_ta); + gsm_chreq_name(rqd->reason), rqd->ref.ra, rqd->ta); info = (struct lchan_activate_info){ .activ_for = FOR_MS_CHANNEL_REQUEST, .chan_mode = GSM48_CMODE_SIGN, }; lchan_activate(lchan, &info); - return 0; + llist_del(&rqd->entry); + talloc_free(rqd); + return; } int rsl_tx_imm_assignment(struct gsm_lchan *lchan) diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c index a17143dc0..ce1b20068 100644 --- a/src/osmo-bsc/bts.c +++ b/src/osmo-bsc/bts.c @@ -263,6 +263,7 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) INIT_LLIST_HEAD(&bts->loc_list); INIT_LLIST_HEAD(&bts->local_neighbors); INIT_LLIST_HEAD(&bts->oml_fail_rep); + INIT_LLIST_HEAD(&bts->chan_rqd_queue); /* Enable all codecs by default. These get reset to a more fine grained selection IF a * 'codec-support' config appears in the config file (see bsc_vty.c). */ diff --git a/src/osmo-bsc/lchan_fsm.c b/src/osmo-bsc/lchan_fsm.c index 115c3da55..65d77c4ac 100644 --- a/src/osmo-bsc/lchan_fsm.c +++ b/src/osmo-bsc/lchan_fsm.c @@ -413,8 +413,24 @@ static void lchan_reset(struct gsm_lchan *lchan) static void lchan_fsm_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { struct gsm_lchan *lchan = lchan_fi_lchan(fi); + struct gsm_bts *bts = lchan->ts->trx->bts; lchan_reset(lchan); osmo_fsm_inst_dispatch(lchan->ts->fi, TS_EV_LCHAN_UNUSED, lchan); + + /* Poll the channel request queue, so that waiting calls can make use of the lchan that just + * has become unused now. */ + abis_rsl_chan_rqd_queue_poll(bts); +} + +static void lchan_fsm_wait_after_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_lchan *lchan = lchan_fi_lchan(fi); + struct gsm_bts *bts = lchan->ts->trx->bts; + + /* We also need to poll the channel request queue when the FSM enters the WAIT_AFTER_ERROR + * state. In case of an emergency call the channel request queue will skip the waiting + * period. */ + abis_rsl_chan_rqd_queue_poll(bts); } /* Configure the multirate setting on this channel. */ @@ -1429,6 +1445,7 @@ static const struct osmo_fsm_state lchan_fsm_states[] = { }, [LCHAN_ST_WAIT_AFTER_ERROR] = { .name = "WAIT_AFTER_ERROR", + .onenter = lchan_fsm_wait_after_error_onenter, .in_event_mask = 0 | S(LCHAN_EV_RTP_RELEASED) /* ignore late lchan_rtp_fsm release events */ , @@ -1496,6 +1513,13 @@ static void lchan_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, } } +void lchan_fsm_skip_error(struct gsm_lchan *lchan) +{ + struct osmo_fsm_inst *fi = lchan->fi; + if (fi->state == LCHAN_ST_WAIT_AFTER_ERROR) + lchan_fsm_state_chg(LCHAN_ST_UNUSED); +} + static int lchan_fsm_timer_cb(struct osmo_fsm_inst *fi) { struct gsm_lchan *lchan = lchan_fi_lchan(fi); diff --git a/src/osmo-bsc/lchan_select.c b/src/osmo-bsc/lchan_select.c index d2dba1bb2..6d3caaced 100644 --- a/src/osmo-bsc/lchan_select.c +++ b/src/osmo-bsc/lchan_select.c @@ -162,15 +162,12 @@ struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts, return lchan_select_by_type(bts, type); } -/* Return a matching lchan from a specific BTS that is currently available. The next logical step is - * lchan_activate() on it, which would possibly cause dynamic timeslot pchan switching, taken care of by - * the lchan and timeslot FSMs. */ -struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type) +struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type) { struct gsm_lchan *lchan = NULL; enum gsm_phys_chan_config first, first_cbch, second, second_cbch; - LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_select_by_type(%s)\n", gsm_lchant_name(type)); + LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_avail_by_type(%s)\n", gsm_lchant_name(type)); switch (type) { case GSM_LCHAN_SDCCH: @@ -231,6 +228,20 @@ struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type LOG_BTS(bts, DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); } + return lchan; +} + +/* Return a matching lchan from a specific BTS that is currently available. The next logical step is + * lchan_activate() on it, which would possibly cause dynamic timeslot pchan switching, taken care of by + * the lchan and timeslot FSMs. */ +struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type) +{ + struct gsm_lchan *lchan = NULL; + + lchan = lchan_avail_by_type(bts, type); + + LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_select_by_type(%s)\n", gsm_lchant_name(type)); + if (lchan) { lchan->type = type; LOG_LCHAN(lchan, LOGL_INFO, "Selected\n");