diff options
author | Harald Welte <laforge@gnumonks.org> | 2008-12-01 01:19:19 +0530 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2008-12-01 01:19:19 +0530 |
commit | fd405f799425b6832a0c9cc7a56b07f43288b8b1 (patch) | |
tree | c02cdfa014a63339950a98ef7e3bdbebb44adbf5 /gsmstack | |
parent | 753c2fc0f6861f6a3dcf6e5520343d93b5ed2317 (diff) |
gsmstack: add new unfinished GSM burst/channel demultiplex
This is some unfinished work on a new codebase for 'proper'
demultiplex of all the various physical and logical channel
types.
Basically this includes everything needed to get from the
differential-decoded bursts up to the MAC blocks that can
be passed up to layer 2.
Diffstat (limited to 'gsmstack')
-rw-r--r-- | gsmstack/cch.c | 483 | ||||
-rw-r--r-- | gsmstack/channel.c | 143 | ||||
-rw-r--r-- | gsmstack/get_lctype.c | 105 | ||||
-rw-r--r-- | gsmstack/get_lctype.h | 7 | ||||
-rw-r--r-- | gsmstack/gsmstack.c | 31 | ||||
-rw-r--r-- | gsmstack/gsmstack.h | 133 | ||||
-rw-r--r-- | gsmstack/gsmstack_chan.h | 12 | ||||
-rw-r--r-- | gsmstack/gsmtap.h | 57 | ||||
-rw-r--r-- | gsmstack/source_pcap_burst.c | 148 |
9 files changed, 1119 insertions, 0 deletions
diff --git a/gsmstack/cch.c b/gsmstack/cch.c new file mode 100644 index 0000000..7a1fd66 --- /dev/null +++ b/gsmstack/cch.c @@ -0,0 +1,483 @@ + +/* This file was taken from gsm-tvoid */ + +#include "system.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <math.h> + +#include "burst_types.h" +#include "cch.h" +#include "fire_crc.h" + +/* + * GSM SACCH -- Slow Associated Control Channel + * + * These messages are encoded exactly the same as on the BCCH. + * (Broadcast Control Channel.) + * + * Input: 184 bits + * + * 1. Add parity and flushing bits. (Output 184 + 40 + 4 = 228 bit) + * 2. Convolutional encode. (Output 228 * 2 = 456 bit) + * 3. Interleave. (Output 456 bit) + * 4. Map on bursts. (4 x 156 bit bursts with each 2x57 bit content data) + */ + + +/* + * Parity (FIRE) for the GSM SACCH channel. + * + * g(x) = (x^23 + 1)(x^17 + x^3 + 1) + * = x^40 + x^26 + x^23 + x^17 + x^3 + 1 + */ + +static const unsigned char parity_polynomial[PARITY_SIZE + 1] = { + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 1 +}; + +// remainder after dividing data polynomial by g(x) +static const unsigned char parity_remainder[PARITY_SIZE] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1 +}; + + +/* +static void parity_encode(unsigned char *d, unsigned char *p) { + + int i; + unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q; + + memcpy(buf, d, DATA_BLOCK_SIZE); + memset(buf + DATA_BLOCK_SIZE, 0, PARITY_SIZE); + + for(q = buf; q < buf + DATA_BLOCK_SIZE; q++) + if(*q) + for(i = 0; i < PARITY_SIZE + 1; i++) + q[i] ^= parity_polynomial[i]; + for(i = 0; i < PARITY_SIZE; i++) + p[i] = !buf[DATA_BLOCK_SIZE + i]; +} + */ + + +static int parity_check(unsigned char *d) { + + unsigned int i; + unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q; + + memcpy(buf, d, DATA_BLOCK_SIZE + PARITY_SIZE); + + for(q = buf; q < buf + DATA_BLOCK_SIZE; q++) + if(*q) + for(i = 0; i < PARITY_SIZE + 1; i++) + q[i] ^= parity_polynomial[i]; + return memcmp(buf + DATA_BLOCK_SIZE, parity_remainder, PARITY_SIZE); +} + + +/* + * Convolutional encoding and Viterbi decoding for the GSM SACCH channel. + */ + +/* + * Convolutional encoding: + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + * + * i.e., + * + * c_{2k} = u_k + u_{k - 3} + u_{k - 4} + * c_{2k + 1} = u_k + u_{k - 1} + u_{k - 3} + u_{k - 4} + */ +#define K 5 +#define MAX_ERROR (2 * CONV_INPUT_SIZE + 1) + + +/* + * Given the current state and input bit, what are the output bits? + * + * encode[current_state][input_bit] + */ +static const unsigned int encode[1 << (K - 1)][2] = { + {0, 3}, {3, 0}, {3, 0}, {0, 3}, + {0, 3}, {3, 0}, {3, 0}, {0, 3}, + {1, 2}, {2, 1}, {2, 1}, {1, 2}, + {1, 2}, {2, 1}, {2, 1}, {1, 2} +}; + + +/* + * Given the current state and input bit, what is the next state? + * + * next_state[current_state][input_bit] + */ +static const unsigned int next_state[1 << (K - 1)][2] = { + {0, 8}, {0, 8}, {1, 9}, {1, 9}, + {2, 10}, {2, 10}, {3, 11}, {3, 11}, + {4, 12}, {4, 12}, {5, 13}, {5, 13}, + {6, 14}, {6, 14}, {7, 15}, {7, 15} +}; + + +/* + * Given the previous state and the current state, what input bit caused + * the transition? If it is impossible to transition between the two + * states, the value is 2. + * + * prev_next_state[previous_state][current_state] + */ +static const unsigned int prev_next_state[1 << (K - 1)][1 << (K - 1)] = { + { 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2}, + { 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2}, + { 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2}, + { 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2}, + { 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2}, + { 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2}, + { 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2}, + { 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2}, + { 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2}, + { 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2}, + { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2}, + { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2}, + { 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2}, + { 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2}, + { 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1}, + { 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1} +}; + + +static inline unsigned int hamming_distance2(unsigned int w) { + + return (w & 1) + !!(w & 2); +} + + +/* +static void conv_encode(unsigned char *data, unsigned char *output) { + + unsigned int i, state = 0, o; + + // encode data + for(i = 0; i < CONV_INPUT_SIZE; i++) { + o = encode[state][data[i]]; + state = next_state[state][data[i]]; + *output++ = !!(o & 2); + *output++ = o & 1; + } +} + */ + + +static int conv_decode(unsigned char *output, unsigned char *data) { + + int i, t; + unsigned int rdata, state, nstate, b, o, distance, accumulated_error, + min_state, min_error, cur_state; + + unsigned int ae[1 << (K - 1)]; + unsigned int nae[1 << (K - 1)]; // next accumulated error + unsigned int state_history[1 << (K - 1)][CONV_INPUT_SIZE + 1]; + + // initialize accumulated error, assume starting state is 0 + for(i = 0; i < (1 << (K - 1)); i++) + ae[i] = nae[i] = MAX_ERROR; + ae[0] = 0; + + // build trellis + for(t = 0; t < CONV_INPUT_SIZE; t++) { + + // get received data symbol + rdata = (data[2 * t] << 1) | data[2 * t + 1]; + + // for each state + for(state = 0; state < (1 << (K - 1)); state++) { + + // make sure this state is possible + if(ae[state] >= MAX_ERROR) + continue; + + // find all states we lead to + for(b = 0; b < 2; b++) { + + // get next state given input bit b + nstate = next_state[state][b]; + + // find output for this transition + o = encode[state][b]; + + // calculate distance from received data + distance = hamming_distance2(rdata ^ o); + + // choose surviving path + accumulated_error = ae[state] + distance; + if(accumulated_error < nae[nstate]) { + + // save error for surviving state + nae[nstate] = accumulated_error; + + // update state history + state_history[nstate][t + 1] = state; + } + } + } + + // get accumulated error ready for next time slice + for(i = 0; i < (1 << (K - 1)); i++) { + ae[i] = nae[i]; + nae[i] = MAX_ERROR; + } + } + + // the final state is the state with the fewest errors + min_state = (unsigned int)-1; + min_error = MAX_ERROR; + for(i = 0; i < (1 << (K - 1)); i++) { + if(ae[i] < min_error) { + min_state = i; + min_error = ae[i]; + } + } + + // trace the path + cur_state = min_state; + for(t = CONV_INPUT_SIZE; t >= 1; t--) { + min_state = cur_state; + cur_state = state_history[cur_state][t]; // get previous + output[t - 1] = prev_next_state[cur_state][min_state]; + } + + // return the number of errors detected (hard-decision) + return min_error; +} + + +/* + * GSM SACCH interleaving and burst mapping + * + * Interleaving: + * + * Given 456 coded input bits, form 4 blocks of 114 bits: + * + * i(B, j) = c(n, k) k = 0, ..., 455 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 4n + (k mod 4) + * j = 2(49k mod 57) + ((k mod 8) div 4) + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where h_l(B) and h_n(B) are bits in burst B indicating flags. + */ + +/* +static void interleave(unsigned char *data, unsigned char *iBLOCK) { + + int j, k, B; + + // for each bit in input data + for(k = 0; k < CONV_SIZE; k++) { + B = k % 4; + j = 2 * ((49 * k) % 57) + ((k % 8) / 4); + iBLOCK[B * iBLOCK_SIZE + j] = data[k]; + } +} + */ + + +#if 0 +static void decode_interleave(unsigned char *data, unsigned char *iBLOCK) { + + int j, k, B; + + for(k = 0; k < CONV_SIZE; k++) { + B = k % 4; + j = 2 * ((49 * k) % 57) + ((k % 8) / 4); + data[k] = iBLOCK[B * iBLOCK_SIZE + j]; + } +} + +#endif + +/* +static void burstmap(unsigned char *iBLOCK, unsigned char *eBLOCK, + unsigned char hl, unsigned char hn) { + + int j; + + for(j = 0; j < 57; j++) { + eBLOCK[j] = iBLOCK[j]; + eBLOCK[j + 59] = iBLOCK[j + 57]; + } + eBLOCK[57] = hl; + eBLOCK[58] = hn; +} + */ + + +static void decode_burstmap(unsigned char *iBLOCK, unsigned char *eBLOCK, + unsigned char *hl, unsigned char *hn) { + + int j; + + for(j = 0; j < 57; j++) { + iBLOCK[j] = eBLOCK[j]; + iBLOCK[j + 57] = eBLOCK[j + 59]; + } + *hl = eBLOCK[57]; + *hn = eBLOCK[58]; +} + + +/* + * Transmitted bits are sent least-significant first. + */ +static int compress_bits(unsigned char *dbuf, unsigned int dbuf_len, + unsigned char *sbuf, unsigned int sbuf_len) { + + unsigned int i, j, c, pos = 0; + + if(dbuf_len < ((sbuf_len + 7) >> 3)) + return -1; + + for(i = 0; i < sbuf_len; i += 8) { + for(j = 0, c = 0; (j < 8) && (i + j < sbuf_len); j++) + c |= (!!sbuf[i + j]) << j; + dbuf[pos++] = c & 0xff; + } + return pos; +} + + +#if 0 +int get_ns_l3_len(unsigned char *data, unsigned int datalen) { + + if((data[0] & 3) != 1) { + fprintf(stderr, "error: get_ns_l3_len: pseudo-length reserved " + "bits bad (%2.2x)\n", data[0] & 3); + return -1; + } + return (data[0] >> 2); +} + +#endif + + +static unsigned char *decode_sacch(GS_CTX *ctx, unsigned char *burst, unsigned int *datalen) { + + int errors, len, data_size; + unsigned char conv_data[CONV_SIZE], iBLOCK[BLOCKS][iBLOCK_SIZE], + hl, hn, decoded_data[PARITY_OUTPUT_SIZE]; + FC_CTX fc_ctx; + + data_size = sizeof ctx->msg; + if(datalen) + *datalen = 0; + + // unmap the bursts + decode_burstmap(iBLOCK[0], burst, &hl, &hn); // XXX ignore stealing bits + decode_burstmap(iBLOCK[1], burst + 116, &hl, &hn); + decode_burstmap(iBLOCK[2], burst + 116 * 2, &hl, &hn); + decode_burstmap(iBLOCK[3], burst + 116 * 3, &hl, &hn); + + // remove interleave + interleave_decode(&ctx->interleave_ctx, conv_data, (unsigned char *)iBLOCK); + //decode_interleave(conv_data, (unsigned char *)iBLOCK); + + // Viterbi decode + errors = conv_decode(decoded_data, conv_data); + if (errors) { + DEBUGF("conv_decode: %d\n", errors); + return NULL; + } + + // check parity + // If parity check error detected try to fix it. + if (parity_check(decoded_data)) + { + FC_init(&fc_ctx, 40, 184); + unsigned char crc_result[224]; + if (FC_check_crc(&fc_ctx, decoded_data, crc_result) == 0) + { + errors = -1; + DEBUGF("error: sacch: parity error (%d)\n", errors); + return NULL; + } else { + DEBUGF("Successfully corrected parity bits!\n"); + memcpy(decoded_data, crc_result, sizeof crc_result); + errors = 0; + } + } + + if((len = compress_bits(ctx->msg, data_size, decoded_data, + DATA_BLOCK_SIZE)) < 0) { + fprintf(stderr, "error: compress_bits\n"); + return NULL; + } + if(len < data_size) { + fprintf(stderr, "error: buf too small (%d < %d)\n", + sizeof(ctx->msg), len); + return NULL; + } + + if(datalen) + *datalen = (unsigned int)len; + return ctx->msg; +} + + +/* + * decode_cch + * + * Decode a "common" control channel. Most control channels use + * the same burst, interleave, Viterbi and parity configuration. + * The documentation for the control channels defines SACCH first + * and then just keeps referring to that. + * + * The current (investigated) list is as follows: + * + * BCCH Norm + * BCCH Ext + * PCH + * AGCH + * CBCH (SDCCH/4) + * CBCH (SDCCH/8) + * SDCCH/4 + * SACCH/C4 + * SDCCH/8 + * SACCH/C8 + * + * We provide two functions, one for where all four bursts are + * contiguous, and one where they aren't. + */ +unsigned char *decode_cch(GS_CTX *ctx, unsigned char *burst, unsigned int *datalen) { + + return decode_sacch(ctx, burst, datalen); +} + + +#if 0 +unsigned char *decode_cch(GS_CTX *ctx, unsigned char *e, unsigned int *datalen) { + + return decode_sacch(ctx, e, e + eBLOCK_SIZE, e + 2 * eBLOCK_SIZE, + e + 3 * eBLOCK_SIZE, datalen); +} +#endif diff --git a/gsmstack/channel.c b/gsmstack/channel.c new file mode 100644 index 0000000..768ed65 --- /dev/null +++ b/gsmstack/channel.c @@ -0,0 +1,143 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "gsmstack.h" + +/* convert an 18byte 8-bit-per-byte burst to a 142byte 1bit-per-byte */ +static void bit_per_byte(unsigned char *dest, const unsigned char *src) +{ + int bit, byte; + + for (byte = 0; byte < BURST_BYTES; src++) { + for (bit = 0; bit < 8; bit++) { + if (src[byte] >> bit &= 0x01) + *dest++ = 0x01; + else + *dest++ = 0x00; + } + } +} + +static int add_burst_to_lchan(struct gsm_logi_chan *lchan, + struct gsm_burst *burst) +{ + int rc = 0; + + /* copy burst to burst buffer */ + memcpy(lchan->burst_buf[lchan->next_burst], burst, sizeof(*burst)); + lchan->next_burst++; + + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + /* FIXME */ + break; + case GSM_LCHAN_TCH_H: + /* FIXME */ + default: + if (lchan->next_burst == 4) { + lchan->next_burst = 0; + /* FIXME: decode the four bursts into a MAC block */ + + /* pass the resulting MAC block up the stack */ + rc = gsm_lchan_macblock() + } + } + + return rc; +} + +static int gsm_rx_sdcch8(struct gsm_burst *burst) +{ + struct gsm_phys_chan *pchan = burst->gsm_pchan; + int rc = -EINVAL; + + if (burst_type == GSM_BURST_DUMMY) + return; + + if (burst->type != GSM_BURST_NORMAL) { + fprintf(stderr, "Burst type %u not allowed in SDCCH8\n", + burst->type); + /* FIXME: statistics */ + return rc; + } + + lchan = get_lchan(pchan, burst->fnr); + return add_burst_to_lchan(lchan, burst); +} + +static int gsm_rx_tch(struct gsm_burst *burst) +{ + struct gsm_phys_chan *pchan = burst->gsm_pchan; + struct gsm_logi_chan *lchan; + int rc = -EINVAL; + + if (burst->type != GSM_BURST_NORMAL) + return rc; + + lchan = get_lchan(pchan, fnr); + return add_burst_to_lchan(lchan, burst); +} + +/* input a new GSM Um burst on a CCCH */ +static int gsm_rx_ccch(struct gsm_burst *burst) +{ + struct gsm_phys_chan *pchan = burst->gsm_pchan; + struct gsm_logi_chan *lchan; + int rc = -EINVAL; + + switch (burst->type) { + case GSM_BURST_FCCH: + /* FIXME */ + /* obtain the frequency offset and report to caller */ + break; + case GSM_BURST_SCH: + /* obtain the RFN from the SCH burst */ + /* FIXME */ + break; + case GSM_BURST_NORMAL: + /* determine logical channel and append burst */ + lchan = get_lchan(pchan, burst->fnr); + rc = add_burst_to_lchan(lchan, burst); + break; + default: + break; + }; + return rc; +} + +/* input a new GSM Um interface burst into the stack */ +int gsm_rx_burst(struct gsm_burst *burst, int bits) +{ + struct gsm_phys_chan *pchan; + int rc = -EINVAL; + + /* we assume the following fields have already been + * filled-in by the caller: + * phys_chan, rx_time, rx_frame_nr, decoded/decoded_bits */ + + pchan = burst->phys_chan; + + if (!bits) + bit_per_byte(burst->decoded_bits, burst->decoded); + + pchan->stats.rx_total++; + pchan->stats.rx_type[burst->type]++; + + switch (pchan->config) { + case GSM_PCHAN_CCCH: + rc = gsm_rx_ccch(burst); + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + rc = gsm_rx_sdcch8(burst); + break; + case GSM_PCHAN_TCH_F: + rc = + break; + case GSM_PCHAN_UNKNOWN: + default: + fprintf(stderr, "unknown pchan config (ts=%u\n)\n", + pchan->timeslot); + return; + break; + } +} diff --git a/gsmstack/get_lctype.c b/gsmstack/get_lctype.c new file mode 100644 index 0000000..953b56d --- /dev/null +++ b/gsmstack/get_lctype.c @@ -0,0 +1,105 @@ + +/* helper routines to determine the logical channel type based on + * physical channel configuration and frame number */ + +#include "gsmstack.h" +#include "get_lctype.h" + +#define GSM_FN_51 (fn / 51) + +#define GSM_TC(fn) ((fn / 51) % 8) + + +/* parameters determined from CCCH_CONF (octet 2 of control channel description + * in System Information Type 3: + * BS_CC_CHANS defines if we have CCCH on ts 2/4/6 + * BS_CCCH_SDCCH_COMB defines if we have SDCCH/8 SACCH/C8 on TS0 + * BS_AG_BLKS_RES defines which CCCH blocks are reserved for AGCH + * if BCCH Ext. is used, BS_AS_BLKS_RES has to be non-zero + */ + +static int get_lctype_for_ccch(unsigned int fnr) +{ + unsigned int fnr51 = GSM_FN_51(fnr); + int lc_type; + + if (fnr51 % 10 == 0) + lc_type = GSM_LCHAN_FCCH; + else if (fnr51 % 10 == 1) + lc_type = GSM_LCHAN_SCH; + else if (fnr51 >= 2 && fnr_mod_51 <= 5) + lc_type = GSM_LCHAN_BCCH; + else if (fnr51 >= 6 && fnr51 <= 9) { + if (flags & CCCH_F_BCCH_EXT) + lc_type = GSM_LCHAN_BCCH; + else + lc_ctype = GSM_LCHAN_PCH; + } else + lc_ctype = GSM_LCHAN_PCH; + + /* FIXME: what about AGCH ? */ + /* FIXME: what about NCH ? */ + /* FIXME: what about CBCH ? */ + + return lc_ctype; +} + +static int get_lctype_for_sdcch8(unsigned int fnr) +{ + unsigned int fnr51 = GSM_FN_51(fnr); + unsigned int fnr102 = fnr % 102; + int lc_type; + + /* the lower 32 bursts are evenly divided between SDCCH8 0..7 */ + if (fnr51 % < 32) { + lc_type = GSM_LCHAN_SDCCH8; + subc = fnr51 / 4; + } else { + /* the upper 16 bursts are bundles of four bursts for + * alternating either SACCH0..3 or SACCH4..7 */ + lc_type = GSM_LCHAN_SACCH8C; + subc = (fnr51 - 32) / 4; + if (fnr102 > 50) + subc += 4; + } + + return lc_type; +} + +static int get_lctype_for_tch_f(unsigned int fnr) +{ + unsigned int fnr52 = fnr % 52; + int lc_type = GSM_LCHAN_TCH; + + /* only burst number 12 and 51 are be SACCH */ + if (fnr52 == 12 || fnr52 == 51) + lc_ctype = GSM_LCHAN_SACCH; + /* burst number 26 and 39 are empty (for measurements) */ + else if (fnr52 == 26 || fnr52 == 39) + lc_ctype = GSM_LCHAN_NONE; + + return lc_type; +} + +int get_lctype(struct gsm_phys_chan *pchan, int fnr) +{ + switch (pchan->config) { + case GSM_PCHAN_CCCH: + return get_lctype_for_ccch(fnr); + break; + case GSM_PCHAN_TCH_F: + return get_lctype_for_tch_f(fnr); + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + return get_lctype_for_sdcch8(fnr); + break; + } + + return -EINVAL; +} + +struct gsm_logi_chan *get_lchan(struct gsm_phys_chan *pchan, int fnr) +{ + int lctype = get_lctype(pchan, fnr); + return pchan->logi_chan[lctype]; +} diff --git a/gsmstack/get_lctype.h b/gsmstack/get_lctype.h new file mode 100644 index 0000000..32164a4 --- /dev/null +++ b/gsmstack/get_lctype.h @@ -0,0 +1,7 @@ +#ifndef _GSM_LCTYPE_H +#define _GSM_LCTYPE_H + +extern int get_lctype(struct gsm_phys_chan *pchan, int fnr); +extern struct gsm_logi_chan *get_lchan(struct gsm_phys_chan *pchan, int fnr); + +#endif /* _GSM_LCTYPE_H */ diff --git a/gsmstack/gsmstack.c b/gsmstack/gsmstack.c new file mode 100644 index 0000000..1690d15 --- /dev/null +++ b/gsmstack/gsmstack.c @@ -0,0 +1,31 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <malloc.h> +#include <errno.h> + +#include "gsmstack.h" + + +struct gsm_rf_chan *gsm_init_rfchan(unsigned int arfcn) +{ + struct gsm_rf_chan *rf; + int i; + + rf = malloc(sizeof(*rf)); + if (!rf) + return NULL; + memset(rf, 0, sizeof(*rf)); + + rf->arfcn = arfcn; + + for (i = 0; i < NR_TIMESLOTS; i++) { + struct gsm_phys_chan *pchan; + + pchan = rf->phys_chan[i]; + pchan->timeslot = i; + pchan->rf_chan = rf; + } + + return rf; +} diff --git a/gsmstack/gsmstack.h b/gsmstack/gsmstack.h new file mode 100644 index 0000000..9986dff --- /dev/null +++ b/gsmstack/gsmstack.h @@ -0,0 +1,133 @@ +#ifndef _GSMSTACK_H +#define _GSMSTACK_H + +#define NR_TIMESLOTS 8 +#define NR_ARFCN 1024 + +#define BURST_BITS (USEFUL_BITS/8 + 1) + +#include "gsmtap.h" +enum gsm_burst_type { + GSM_BURST_UNKNOWN = GSMTAP_BURST_UNKNOWN, + GSM_BURST_FCCH = GSMTAP_BURST_FCCH, + GSM_BURST_PARTIAL_FCCH = GSMTAP_BURST_PARTIAL_FCCH, + GSM_BURST_SCH = GSMTAP_BURST_SCH, + GSM_BURST_CTS_SCH = GSMTAP_BURST_CTS_SCH, + GSM_BURST_COMPACT_SCH = GSMTAP_BURST_COMPACT_SCH, + GSM_BURST_NORMAL = GSMTAP_BURST_NORMAL, + GSM_BURST_DUMMY = GSMTAP_BURST_DUMMY, + GSM_BURST_ACCESS = GSMTAP_BURST_ACCESS, + GSM_BURST_NONE = GSMTAP_BURST_NONE, + _GSM_BURST_CNT +}; + + +struct gsm_burst { + /* time at which we were received */ + struct timeval rx_time; + + /* the physical channel which we're part of. + * always guaranteed to be != NULL */ + struct gsm_phys_chan *phys_chan; + + /* the logical channel to which we belong. + * only filled-in if we actually know about it */ + struct gsm_logi_chan *logi_chan; + + /* the burst type (as per gsmtap.h) */ + unsigned char burst_type; + + /* the relative receive TDMA frame */ + unsigned int rx_frame_nr; + + /* the timeslot number is part of the phys_chan */ + + /* the burst after differential decode, 8 bit per byte */ + unsigned char decoded[BURST_BITS]; + + /* the burst after differential decode, 1 bit per byte */ + unsigned char decoded_bits[USEFUL_BITS]; +}; + +struct gsm_burst_stats { + unsigned int rx_total; + unsigned int rx_type[_GSM_BURST_CNT]; +}; + +enum gsm_logical_channel_type { + GSM_LCHAN_UNKNOWN, + GSM_LCHAN_NONE, + + /* CBCH */ + GSM_LCHAN_FCCH, /* Frequency Correction CH */ + GSM_LCHAN_SCH, /* Synchronization CH */ + GSM_LCHAN_BCCH, /* Broadcast Control CH */ + GSM_LCHAN_PCH, /* Paging CH */ + GSM_LCHAN_NCH, /* Notification CH */ + GSM_LCHAN_AGCH, /* Access Grant CH */ + GSM_LCHAN_CBCH, /* Cell Broadcast CH */ + + /* SDCCH */ + GSM_LCHAN_SDCCH8, /* Slow Dedicated Control CH */ + GSM_LCHAN_SACCH8C, /* Slow Associated Control CH */ + + /* TCH */ + GSM_LCHAN_TCH, /* Traffic CH */ + GSM_LCHAN_SACCH, /* Slow Associated Control CH */ + + /* uplink */ + GSM_LCHAN_RACH, /* Random Access CH */ +}; + +struct gsm_logi_chan { + enum gsm_logical_channel_type type; + /* here we aggregate the bursts for this logical channel + * until we have found enough bursts for one MAC block */ + struct gsm_burst burst_buf[4]; + int next_burst; + + struct gsm_burst_stats stats; +}; + +enum gsm_phys_chan_config { + GSM_PCHAN_UNKNOWN, + GSM_PCHAN_CCCH, + GSM_PCHAN_CCCH_WITH_SDCCH8, + GSM_PCHAN_TCH_F, + GSM_PCHAN_TCH_H, + GSM_PCHAN_SDCCH8_SACCH8C, +}; + +/* maximum logical channels for one physical channel */ +#define NR_LOGI_CHANS 16 + +/* A GSM physical channel configuration */ +struct gsm_phys_chan { + /* in which timeslot is this channel? */ + unsigned int timeslot; + /* to which RF channel do we belong? */ + struct gsm_rf_chan *rf_chan; + /* how is our physical configuration */ + enum gsm_phys_chan_config config; + /* those are our logical channels */ + struct gsm_logi_chan logi_chan[NR_LOGI_CHANS]; + int nr_logi_chans; + + struct gsm_burst_stats stats; +}; + +struct gsm_rf_chan { + /* ARFCN (frequency) of the RF channel */ + unsigned int arfcn; + + /* current RFN as determined by SCH + frame count */ + unsigned int rfn; + + /* the physical channel for each timeslot */ + struct gsm_phys_chan phys_chan[NR_TIMESLOTS]; +}; + + +extern struct gsm_rf_chan *gsm_init_rfchan(unsigned int arfcn); + +#endif /* _GSMSTACK_H */ diff --git a/gsmstack/gsmstack_chan.h b/gsmstack/gsmstack_chan.h new file mode 100644 index 0000000..a54fae0 --- /dev/null +++ b/gsmstack/gsmstack_chan.h @@ -0,0 +1,12 @@ +#ifndef _GSMSTACK_CHAN_H +#define _GSMSTACK_CHAN_H + +/* 51 multiframe for CCCH TS0 of a FCCH+SCH+BCCH+PACH */ +const char ccch[] = + "FSBBBBPPPPFSPPPPPPPPFSPPPPPPPPFSPPPPPPPPFSPPPPPPPP_"; + +/* SDCCH/8 + SACCH/C8; A...D are alternating 0..3/4..7 */ +const char sdcch8[] = + "00001111222233334444555566667777AAAABBBBCCCCDDDD___"; + +#endif /* _GSMSTACK_CHAN_H */ diff --git a/gsmstack/gsmtap.h b/gsmstack/gsmtap.h new file mode 100644 index 0000000..20a6074 --- /dev/null +++ b/gsmstack/gsmtap.h @@ -0,0 +1,57 @@ +#ifndef _GSMTAP_H +#define _GSMTAP_H + +/* gsmtap header, pseudo-header in front of the actua GSM payload*/ + +#include <sys/types.h> + +#define GSMTAP_VERSION 0x01 + +#define GSMTAP_TYPE_UM 0x01 +#define GSMTAP_TYPE_ABIS 0x02 +#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */ + +#define GSMTAP_BURST_UNKNOWN 0x00 +#define GSMTAP_BURST_FCCH 0x01 +#define GSMTAP_BURST_PARTIAL_SCH 0x02 +#define GSMTAP_BURST_SCH 0x03 +#define GSMTAP_BURST_CTS_SCH 0x04 +#define GSMTAP_BURST_COMPACT_SCH 0x05 +#define GSMTAP_BURST_NORMAL 0x06 +#define GSMTAP_BURST_DUMMY 0x07 +#define GSMTAP_BURST_ACCESS 0x08 +#define GSMTAP_BURST_NONE 0x09 + +struct gsmtap_hdr { + u_int8_t version; /* version, set to 0x01 currently */ + u_int8_t hdr_len; /* length in number of 32bit words */ + u_int8_t type; /* see GSMTAP_TYPE_* */ + u_int8_t timeslot; /* timeslot (0..7 on Um) */ + + u_int16_t arfcn; /* ARFCN (frequency) */ + u_int8_t noise_db; /* noise figure in dB */ + u_int8_t signal_db; /* signal level in dB */ + + u_int32_t frame_number; /* GSM Frame Number (FN) */ + + u_int8_t burst_type; /* Type of burst, see above */ + u_int8_t antenna_nr; /* Antenna Number */ + u_int16_t res; /* reserved for future use (RFU) */ + +} __attribute__((packed)); + + +/* PCAP related definitions */ +#define TCPDUMP_MAGIC 0xa1b2c3d4 +struct pcap_timeval { + int32_t tv_sec; + int32_t tv_usec; +}; + +struct pcap_sf_pkthdr { + struct pcap_timeval ts; /* time stamp */ + u_int32_t caplen; /* lenght of portion present */ + u_int32_t len; /* length of this packet */ +}; + +#endif /* _GSMTAP_H */ diff --git a/gsmstack/source_pcap_burst.c b/gsmstack/source_pcap_burst.c new file mode 100644 index 0000000..bb340e7 --- /dev/null +++ b/gsmstack/source_pcap_burst.c @@ -0,0 +1,148 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pcap.h> + +#include "gsmstack.h" +#include "gsmtap.h" + +/* source_pcap_burst.c: read a PCAP file with GSMTAP_UM_BURST packets + * and feed them into the stack. + * + * (C) 2008 by Harald Welte <laforge@gnumonks.org> + * + * TODO: + * fix PCAP endianness + */ + + +static struct gsm_rf_chan rfchans[NR_ARFCN]; + +static int read_pcap_hdr(int fd) +{ + int rc; + struct pcap_file_header pfh; + + rc = read(fd, &pfh, sizeof(pfh)); + if (rc < sizeof(pfh)) + return -EIO; + + if (pfh.magic != TCPDUMP_MAGIC) + return -EINVAL; + + if (pfh.linktype != LINKTYPE_GSMTAP) + return -EINVAL; + + return 0; +} + +static int send_burst(struct gsmtap_hdr *gh, int burst_len, + struct timeval *tv) +{ + unsigned char *data = (unsigned char *)gh + sizeof(*gh); + struct gsm_burst burst; + struct gsm_rf_chan *rfchan; + struct gsm_phys_chan *pchan; + unsigned int arfcn; + + arfcn = ntohs(gh->arfcn); + + /* Create new RF channel structure if we've not seen this ARFCN + * before */ + if (!rfchans[arfcn]) { + rfchans[arfcn] = gsm_init_rfchan(arfcn); + if (!rfchans[arfcn]) { + fprintf(stderr, "cannot init rfchan ARFCN=%u\n", + arfcn); + return -EINVAL; + } + } + + rfchan = rfchans[arfcn]; + pchan = rfchan->phys_chan[gh->timeslot]; + + memset(&burst, 0, sizeof(burst)); + burst.phys_chan = pchan; + burst.burst_type = gh->burst_type; + burst.rx_frame_nr = ntohl(gh->frame_number); + memcpy(&burst.rx_time, tv, sizeof(burst.rx_time)); + memcpy(burst.decoded, data, burst_len); + + return gsm_rx_burst(&burst, 0); +} + +/* fills 'buf' with gsmtap_hdr and payload, returns length of payload */ +static int read_one_pkt(int fd, unsigned char *buf, int bufsize, + struct timeval *tv) +{ + struct pcap_sf_pkthdr pkthdr; + struct gsmtap_hdr *gh = (struct gsmtap_hdr *) buf; + int len, burstlen; + + len = read(fd, &pkthdr, sizeof(pkthdr)); + if (len < sizeof(pkthdr)) + return -1; + + /* FIXME: ntohl on caplen and len? */ + + if (pkthdr.caplen < sizeof(*gh)) + return -2; + + if (pkthdr.len > pkthdr.caplen) + return -3; + + if (bufsize < pkthdr.len) + return -4; + + if (tv) { + tv.tv_sec = pkthdr.tv_sec; + tv.tv_usec = pkthdr.tv_usec; + } + + len = read(fd, buf, pkthdr.caplen) + if (len < pkthdr.caplen) + return -5; + + if (gh->version != GSMTAP_VERSION || + gh->type != GSMTAP_TYPE_UM_BURST) + return -6; + + return pkthdr.caplen - sizeof(*gh); +} + +int main(int argc, char **argv) +{ + char *fname; + int fd, rc; + + if (argc != 2) { + fprintf(stderr, "please specify pcap filename\n"); + exit(2); + } + + fname = argv[1]; + + fd = open(fname, O_RDONLY); + if (fd < 0) { + perror("open"); + exit(1); + } + + rc = read_pcap_hdr(fd); + if (rc < 0) { + perror("pcap_hdr"); + exit(1); + } + + while (1) { + unsigned char buf[1024]; + struct timeval tv; + int burst_len; + burst_len = read_one_pkt(fd, &tv, sbuf, sizeof(buf)); + if (burst_len < 0) { + fprintf(stderr, "read_one_pkt() = %d\n", burst_len); + exit(3); + } + rc = send_burst((struct gsmtap_hdr *)buf, burst_len, &tv); + } +} |