/* TETRA LLC Layer */ /* (C) 2011 by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include "tetra_llc_pdu.h" #include "tetra_mle.h" #include "tuntap.h" static int tun_fd = -1; static struct tllc_state g_llcs = { .rx.defrag_list = LLIST_HEAD_INIT(g_llcs.rx.defrag_list), }; static struct tllc_defrag_q_e * get_dqe_for_ns(struct tllc_state *llcs, uint8_t ns, int alloc_if_missing) { struct tllc_defrag_q_e *dqe; llist_for_each_entry(dqe, &llcs->rx.defrag_list, list) { if (dqe->ns == ns) return dqe; } if (alloc_if_missing) { dqe = talloc_zero(NULL, struct tllc_defrag_q_e); dqe->ns = ns; dqe->tl_sdu = msgb_alloc(4096, "LLC defrag"); llist_add(&dqe->list, &llcs->rx.defrag_list); } else dqe = NULL; return dqe; } static int tllc_defrag_in(struct tllc_state *llcs, struct tetra_llc_pdu *lpp, struct msgb *msg, unsigned int len) { struct tllc_defrag_q_e *dqe; dqe = get_dqe_for_ns(llcs, lpp->ns, 1); /* check if this is the first segment, or the next * expected segment */ if (!dqe->last_ss || (dqe->last_ss == lpp->ss - 1)) { /* FIXME: append */ printf("<> ", lpp->ss); dqe->last_ss = lpp->ss; memcpy(msgb_put(dqe->tl_sdu, len), msg->l3h, len); } else printf("<> ", dqe->last_ss, lpp->ss); return 0; } static int tllc_defrag_out(struct tetra_mac_state *tms, struct tllc_state *llcs, struct tetra_llc_pdu *lpp) { struct tllc_defrag_q_e *dqe; struct msgb *msg; dqe = get_dqe_for_ns(llcs, lpp->ns, 0); msg = dqe->tl_sdu; printf("<> "); msg->l3h = msg->data; rx_tl_sdu(tms, msg, msgb_l3len(msg)); if (tun_fd < 0) { tun_fd = tun_alloc("tun0"); fprintf(stderr, "tun_fd=%d\n", tun_fd); } if (tun_fd >= 0) { uint8_t buf[4096]; int len = osmo_ubit2pbit(buf, msg->l3h+3+4+4+4+4, msgb_l3len(msg)-3-4-4-4-4); write(tun_fd, buf, len); } llist_del(&dqe->list); talloc_free(msg); talloc_free(dqe); return 0; } /* Receive TM-SDU (MAC SDU == LLC PDU) */ /* this resembles TMA-UNITDATA.ind (TM-SDU / length) */ int rx_tm_sdu(struct tetra_mac_state *tms, struct msgb *msg, unsigned int len) { if (!len) { return -1; } else if (len < 4) { printf("WARNING rx_tm_sdu: l2len too small: %d\n", len); return -1; } struct tetra_llc_pdu lpp; memset(&lpp, 0, sizeof(lpp)); tetra_llc_pdu_parse(&lpp, msg->l2h, len); msg->l3h = lpp.tl_sdu; msg->tail = msg->l3h + lpp.tl_sdu_len; // Strips off FCS (if present) msg->len = msg->tail - msg->head; printf("TM-SDU(%s)", tetra_get_llc_pdut_dec_name(lpp.pdu_type)); if (lpp.have_fcs) { printf(" fcs=%s ", (lpp.have_fcs && lpp.fcs_invalid ? "BAD" : "OK")); } printf(" l3len=%d", msgb_l3len(msg)); if (msgb_l3len(msg)) { printf(" %s", osmo_ubit_dump(msg->l3h, msgb_l3len(msg))); } printf("\n"); if (!lpp.tl_sdu_len) { return len; } switch (lpp.pdu_type) { case TLLC_PDUT_DEC_BL_ADATA: case TLLC_PDUT_DEC_BL_DATA: case TLLC_PDUT_DEC_BL_UDATA: case TLLC_PDUT_DEC_BL_ACK: case TLLC_PDUT_DEC_AL_SETUP: case TLLC_PDUT_DEC_AL_ACK: case TLLC_PDUT_DEC_AL_RNR: case TLLC_PDUT_DEC_AL_RECONNECT: case TLLC_PDUT_DEC_AL_DISC: /* directly hand it to MLE */ rx_tl_sdu(tms, msg, lpp.tl_sdu_len); break; case TLLC_PDUT_DEC_AL_DATA: case TLLC_PDUT_DEC_AL_UDATA: case TLLC_PDUT_DEC_ALX_DATA: case TLLC_PDUT_DEC_ALX_UDATA: /* input into LLC defragmenter */ tllc_defrag_in(&g_llcs, &lpp, msg, len); break; case TLLC_PDUT_DEC_AL_FINAL: case TLLC_PDUT_DEC_AL_UFINAL: case TLLC_PDUT_DEC_ALX_FINAL: case TLLC_PDUT_DEC_ALX_UFINAL: /* input into LLC defragmenter */ tllc_defrag_in(&g_llcs, &lpp, msg, len); /* check if the fragment is complete and hand it off*/ tllc_defrag_out(tms, &g_llcs, &lpp); break; case TLLC_PDUT_DEC_UNKNOWN: case TLLC_PDUT_DEC_ALX_ACK: case TLLC_PDUT_DEC_ALX_RNR: /* fixme: unhandled types */ break; } return len; }