From 94633f3e2b9d6ccfad93a5d518e4b83c5802c7c1 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 4 May 2010 07:41:59 +0200 Subject: [gprs] Move all GPRS related code to src/gprs subdirectory --- openbsc/src/gprs/gprs_ns.c | 644 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 644 insertions(+) create mode 100644 openbsc/src/gprs/gprs_ns.c (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c new file mode 100644 index 00000000..3d9bb896 --- /dev/null +++ b/openbsc/src/gprs/gprs_ns.c @@ -0,0 +1,644 @@ +/* GPRS Networks Service (NS) messages on the Gb interfacebvci = msgb_bvci(msg); + * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ + +/* (C) 2009-2010 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 General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* Some introduction into NS: NS is used typically on top of frame relay, + * but in the ip.access world it is encapsulated in UDP packets. It serves + * as an intermediate shim betwen BSSGP and the underlying medium. It doesn't + * do much, apart from providing congestion notification and status indication. + * + * Terms: + * NS Network Service + * NSVC NS Virtual Connection + * NSEI NS Entity Identifier + * NSVL NS Virtual Link + * NSVLI NS Virtual Link Identifier + * BVC BSSGP Virtual Connection + * BVCI BSSGP Virtual Connection Identifier + * NSVCG NS Virtual Connection Goup + * Blocked NS-VC cannot be used for user traffic + * Alive Ability of a NS-VC to provide communication + * + * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will + * therefore identify the BSSGP virtual connection by a BVCI passed down to NS. + * NS then has to firgure out which NSVC's are responsible for this BVCI. + * Those mappings are administratively configured. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NS_ALLOC_SIZE 1024 + +static const struct tlv_definition ns_att_tlvdef = { + .def = { + [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 }, + }, +}; + +/* Lookup struct gprs_nsvc based on NSVCI */ +static struct gprs_nsvc *nsvc_by_nsvci(struct gprs_ns_inst *nsi, + uint16_t nsvci) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->nsvci == nsvci) + return nsvc; + } + return NULL; +} + +/* Lookup struct gprs_nsvc based on NSVCI */ +static struct gprs_nsvc *nsvc_by_nsei(struct gprs_ns_inst *nsi, + uint16_t nsei) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->nsei == nsei) + return nsvc; + } + return NULL; +} + + +/* Lookup struct gprs_nsvc based on remote peer socket addr */ +static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi, + struct sockaddr_in *sin) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (!memcmp(&nsvc->ip.bts_addr, sin, sizeof(*sin))) + return nsvc; + } + return NULL; +} + +static void gprs_ns_timer_cb(void *data); + +static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) +{ + struct gprs_nsvc *nsvc; + + nsvc = talloc_zero(nsi, struct gprs_nsvc); + nsvc->nsvci = nsvci; + /* before RESET procedure: BLOCKED and DEAD */ + nsvc->state = NSE_S_BLOCKED; + nsvc->nsi = nsi; + nsvc->timer.cb = gprs_ns_timer_cb; + nsvc->timer.data = nsvc; + + llist_add(&nsvc->list, &nsi->gprs_nsvcs); + + return nsvc; +} + +/* Section 10.3.2, Table 13 */ +static const struct value_string ns_cause_str[] = { + { NS_CAUSE_TRANSIT_FAIL, "Transit network failure" }, + { NS_CAUSE_OM_INTERVENTION, "O&M intervention" }, + { NS_CAUSE_EQUIP_FAIL, "Equipment failure" }, + { NS_CAUSE_NSVC_BLOCKED, "NS-VC blocked" }, + { NS_CAUSE_NSVC_UNKNOWN, "NS-VC unknown" }, + { NS_CAUSE_BVCI_UNKNOWN, "BVCI unknown" }, + { NS_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" }, + { NS_CAUSE_PDU_INCOMP_PSTATE, "PDU not compatible with protocol state" }, + { NS_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, + { NS_CAUSE_INVAL_ESSENT_IE, "Invalid essential IE" }, + { NS_CAUSE_MISSING_ESSENT_IE, "Missing essential IE" }, + { 0, NULL } +}; + +const char *gprs_ns_cause_str(enum ns_cause cause) +{ + return get_value_string(ns_cause_str, cause); +} + +static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); + +static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + int ret; + + switch (nsvc->nsi->ll) { + case GPRS_NS_LL_UDP: + ret = nsip_sendmsg(nsvc, msg); + break; + default: + LOGP(DGPRS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->nsi->ll); + msgb_free(msg); + ret = -EIO; + break; + } + return ret; +} + +static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + + if (!msg) + return -ENOMEM; + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + + nsh->pdu_type = pdu_type; + + return gprs_ns_tx(nsvc, msg); +} + +static int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + uint16_t nsvci = htons(nsvc->nsvci); + uint16_t nsei = htons(nsvc->nsei); + + if (!msg) + return -ENOMEM; + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + nsh->pdu_type = NS_PDUT_RESET; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); + msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei); + + return gprs_ns_tx(nsvc, msg); + +} + +#define NS_ALIVE_RETRIES 10 /* after 3 failed retransmit we declare BTS as dead */ + +static const uint8_t timer_mode_tout[_NSVC_TIMER_NR] = { + [NSVC_TIMER_TNS_RESET] = 60, + [NSVC_TIMER_TNS_ALIVE] = 3, + [NSVC_TIMER_TNS_TEST] = 30, +}; + +static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode) +{ + nsvc->alive_retries = 0; + + if (bsc_timer_pending(&nsvc->timer)) + bsc_del_timer(&nsvc->timer); + + nsvc->timer_mode = mode; + bsc_schedule_timer(&nsvc->timer, timer_mode_tout[mode], 0); +} + +static void gprs_ns_timer_cb(void *data) +{ + struct gprs_nsvc *nsvc = data; + + switch (nsvc->timer_mode) { + case NSVC_TIMER_TNS_ALIVE: + /* Tns-alive case: we expired without response ! */ + nsvc->alive_retries++; + if (nsvc->alive_retries > NS_ALIVE_RETRIES) { + /* mark as dead and blocked */ + nsvc->state = NSE_S_BLOCKED; + DEBUGP(DGPRS, "NSEI=%u Tns-alive expired more then " + "%u times, blocking NS-VC\n", nsvc->nsei, + NS_ALIVE_RETRIES); + /* FIXME: inform higher layers */ + return; + } + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + break; + case NSVC_TIMER_TNS_TEST: + /* Tns-test case: send NS-ALIVE PDU */ + gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); + /* start Tns-alive timer */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + break; + case NSVC_TIMER_TNS_RESET: + /* Chapter 7.3: Re-send the RESET */ + gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION); + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); + break; + } +} + +/* Section 9.2.6 */ +static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + uint16_t nsvci, nsei; + + if (!msg) + return -ENOMEM; + + nsvci = htons(nsvc->nsvci); + nsei = htons(nsvc->nsei); + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + + nsh->pdu_type = NS_PDUT_RESET_ACK; + + DEBUGP(DGPRS, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); + msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei); + + return gprs_ns_tx(nsvc, msg); +} + +/* Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive */ +int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) +{ + struct gprs_nsvc *nsvc; + struct gprs_ns_hdr *nsh; + uint16_t bvci = msgb_bvci(msg); + + nsvc = nsvc_by_nsei(nsi, msgb_nsei(msg)); + if (!nsvc) { + LOGP(DGPRS, LOGL_ERROR, "Unable to resolve NSEI %u " + "to NS-VC!\n", msgb_nsei(msg)); + return -EINVAL; + } + + if (!(nsvc->state & NSE_S_ALIVE)) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u is not alive, cannot send\n", + nsvc->nsei); + return -EBUSY; + } + if (nsvc->state & NSE_S_BLOCKED) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u is blocked, cannot send\n", + nsvc->nsei); + return -EBUSY; + } + + nsh = (struct gprs_ns_hdr *) msgb_push(msg, sizeof(*nsh) + 3); + if (!nsh) { + LOGP(DGPRS, LOGL_ERROR, "Not enough headroom for NS header\n"); + return -EIO; + } + + nsh->pdu_type = NS_PDUT_UNITDATA; + /* spare octet in data[0] */ + nsh->data[1] = bvci >> 8; + nsh->data[2] = bvci & 0xff; + + return gprs_ns_tx(nsvc, msg); +} + +/* Section 9.2.10: receive side */ +static int gprs_ns_rx_unitdata(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; + uint16_t bvci; + + /* spare octet in data[0] */ + bvci = nsh->data[1] << 8 | nsh->data[2]; + msgb_bssgph(msg) = &nsh->data[3]; + msgb_bvci(msg) = bvci; + + /* call upper layer (BSSGP) */ + return nsvc->nsi->cb(GPRS_NS_EVT_UNIT_DATA, nsvc, msg, bvci); +} + +/* Section 9.2.7 */ +static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct tlv_parsed tp; + uint8_t cause; + int rc; + + DEBUGP(DGPRS, "NSEI=%u NS STATUS ", nsvc->nsei); + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) { + DEBUGPC(DGPRS, "missing cause IE\n"); + return -EINVAL; + } + + cause = *TLVP_VAL(&tp, NS_IE_CAUSE); + DEBUGPC(DGPRS, "cause=%s\n", gprs_ns_cause_str(cause)); + + return 0; +} + +/* Section 7.3 */ +static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct tlv_parsed tp; + uint8_t *cause; + uint16_t *nsvci, *nsei; + int rc; + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || + !TLVP_PRESENT(&tp, NS_IE_VCI) || + !TLVP_PRESENT(&tp, NS_IE_NSEI)) { + /* FIXME: respond with NS_CAUSE_MISSING_ESSENT_IE */ + LOGP(DGPRS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + return -EINVAL; + } + + cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); + nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); + nsei = (uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI); + + DEBUGP(DGPRS, "NSEI=%u NS RESET (NSVCI=%u, cause=%s)\n", + nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause)); + + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + nsvc->nsei = ntohs(*nsei); + nsvc->nsvci = ntohs(*nsvci); + + /* mark the NS-VC as blocked and alive */ + /* start the test procedure */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + + return gprs_ns_tx_reset_ack(nsvc); +} + +/* main entry point, here incoming NS frames enter */ +int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, + struct sockaddr_in *saddr) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct gprs_nsvc *nsvc; + int rc = 0; + + /* look up the NSVC based on source address */ + nsvc = nsvc_by_rem_addr(nsi, saddr); + if (!nsvc) { + /* Only the RESET procedure creates a new NSVC */ + if (nsh->pdu_type != NS_PDUT_RESET) { + LOGP(DGPRS, LOGL_INFO, "Ignoring NS PDU type 0x%0x " + "from %s for non-existing NS-VC\n", + nsh->pdu_type, inet_ntoa(saddr->sin_addr)); + //gprs_ns_tx_reset(nsvc, NS_CAUSE_NSVC_UNKNOWN); + return -EIO; + } + LOGP(DGPRS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", + inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); + nsvc = nsvc_create(nsi, 0xffff); + nsvc->ip.bts_addr = *saddr; + } else + msgb_nsei(msg) = nsvc->nsei; + + switch (nsh->pdu_type) { + case NS_PDUT_ALIVE: + /* remote end inquires whether we're still alive, + * we need to respond with ALIVE_ACK */ + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE_ACK); + break; + case NS_PDUT_ALIVE_ACK: + /* stop Tns-alive */ + bsc_del_timer(&nsvc->timer); + /* start Tns-test */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST); + if (nsvc->remote_end_is_sgsn) { + /* FIXME: this should be one level higher */ + if (nsvc->state & NSE_S_BLOCKED) + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK); + } + break; + case NS_PDUT_UNITDATA: + /* actual user data */ + rc = gprs_ns_rx_unitdata(nsvc, msg); + break; + case NS_PDUT_STATUS: + rc = gprs_ns_rx_status(nsvc, msg); + break; + case NS_PDUT_RESET: + rc = gprs_ns_rx_reset(nsvc, msg); + break; + case NS_PDUT_RESET_ACK: + DEBUGP(DGPRS, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); + /* mark remote NS-VC as blocked + active */ + nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; + if (nsvc->remote_end_is_sgsn) { + /* stop RESET timer */ + bsc_del_timer(&nsvc->timer); + /* send ALIVE PDU */ + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + /* mark local state as BLOCKED + ALIVE */ + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + } + break; + case NS_PDUT_UNBLOCK: + /* Section 7.2: unblocking procedure */ + DEBUGP(DGPRS, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei); + nsvc->state &= ~NSE_S_BLOCKED; + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); + break; + case NS_PDUT_UNBLOCK_ACK: + DEBUGP(DGPRS, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei); + /* mark remote NS-VC as unblocked + active */ + nsvc->remote_state = NSE_S_ALIVE; + if (nsvc->remote_end_is_sgsn) + nsvc->state = NSE_S_ALIVE; + break; + case NS_PDUT_BLOCK: + DEBUGP(DGPRS, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); + nsvc->state |= NSE_S_BLOCKED; + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); + break; + case NS_PDUT_BLOCK_ACK: + DEBUGP(DGPRS, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei); + /* mark remote NS-VC as blocked + active */ + nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; + break; + default: + DEBUGP(DGPRS, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n", + nsvc->nsei, nsh->pdu_type); + rc = -EINVAL; + break; + } + return rc; +} + +struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb) +{ + struct gprs_ns_inst *nsi = talloc_zero(tall_bsc_ctx, struct gprs_ns_inst); + + nsi->cb = cb; + INIT_LLIST_HEAD(&nsi->gprs_nsvcs); + + return nsi; +} + +void gprs_ns_destroy(struct gprs_ns_inst *nsi) +{ + /* FIXME: clear all timers */ + + /* recursively free the NSI and all its NSVCs */ + talloc_free(nsi); +} + + +/* NS-over-IP code, according to 3GPP TS 48.016 Chapter 6.2 + * We don't support Size Procedure, Configuration Procedure, ChangeWeight Procedure */ + +/* Read a single NS-over-IP message */ +static struct msgb *read_nsip_msg(struct bsc_fd *bfd, int *error, + struct sockaddr_in *saddr) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Abis/IP/GPRS-NS"); + int ret = 0; + socklen_t saddr_len = sizeof(*saddr); + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, + (struct sockaddr *)saddr, &saddr_len); + if (ret < 0) { + LOGP(DGPRS, LOGL_ERROR, "recv error %s during NSIP recv\n", + strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msg->l2h = msg->data; + msgb_put(msg, ret); + + return msg; +} + +static int handle_nsip_read(struct bsc_fd *bfd) +{ + int error; + struct sockaddr_in saddr; + struct gprs_ns_inst *nsi = bfd->data; + struct msgb *msg = read_nsip_msg(bfd, &error, &saddr); + + if (!msg) + return error; + + return gprs_ns_rcvmsg(nsi, msg, &saddr); +} + +static int handle_nsip_write(struct bsc_fd *bfd) +{ + /* FIXME: actually send the data here instead of nsip_sendmsg() */ + return -EIO; +} + +int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + int rc; + struct gprs_ns_inst *nsi = nsvc->nsi; + struct sockaddr_in *daddr = &nsvc->ip.bts_addr; + + rc = sendto(nsi->nsip.fd.fd, msg->data, msg->len, 0, + (struct sockaddr *)daddr, sizeof(*daddr)); + + talloc_free(msg); + + return rc; +} + +/* UDP Port 23000 carries the LLC-in-BSSGP-in-NS protocol stack */ +static int nsip_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_nsip_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_nsip_write(bfd); + + return rc; +} + + +/* FIXME: this is currently in input/ipaccess.c */ +extern int make_sock(struct bsc_fd *bfd, int proto, uint16_t port, + int (*cb)(struct bsc_fd *fd, unsigned int what)); + +/* Listen for incoming GPRS packets */ +int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port) +{ + int ret; + + ret = make_sock(&nsi->nsip.fd, IPPROTO_UDP, udp_port, nsip_fd_cb); + if (ret < 0) + return ret; + + nsi->ll = GPRS_NS_LL_UDP; + nsi->nsip.fd.data = nsi; + + return ret; +} + +/* Establish a connection (from the BSS) to the SGSN */ +struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, + struct sockaddr_in *dest, uint16_t nsei, + uint16_t nsvci) +{ + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_rem_addr(nsi, dest); + if (!nsvc) { + nsvc = nsvc_create(nsi, nsvci); + nsvc->ip.bts_addr = *dest; + } + nsvc->nsei = nsei; + nsvc->nsvci = nsvci; + nsvc->remote_end_is_sgsn = 1; + + /* Initiate a RESET procedure */ + if (gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION) < 0) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n", + nsei); + } + /* run a timer and re-transmit the reset request? */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); + + return nsvc; +} -- cgit v1.2.3 From b8a6a83be64ff93993d442fcea8b10962f646c10 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 11 May 2010 05:54:22 +0200 Subject: [gprs] Add new 'NS' and 'BSSGP' logging categories --- openbsc/src/gprs/gprs_ns.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 3d9bb896..be3d1d93 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -160,7 +160,7 @@ static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg) ret = nsip_sendmsg(nsvc, msg); break; default: - LOGP(DGPRS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->nsi->ll); + LOGP(DNS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->nsi->ll); msgb_free(msg); ret = -EIO; break; @@ -234,7 +234,7 @@ static void gprs_ns_timer_cb(void *data) if (nsvc->alive_retries > NS_ALIVE_RETRIES) { /* mark as dead and blocked */ nsvc->state = NSE_S_BLOCKED; - DEBUGP(DGPRS, "NSEI=%u Tns-alive expired more then " + DEBUGP(DNS, "NSEI=%u Tns-alive expired more then " "%u times, blocking NS-VC\n", nsvc->nsei, NS_ALIVE_RETRIES); /* FIXME: inform higher layers */ @@ -273,7 +273,7 @@ static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) nsh->pdu_type = NS_PDUT_RESET_ACK; - DEBUGP(DGPRS, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n", + DEBUGP(DNS, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n", nsvc->nsei, nsvc->nsvci); msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); @@ -291,25 +291,25 @@ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) nsvc = nsvc_by_nsei(nsi, msgb_nsei(msg)); if (!nsvc) { - LOGP(DGPRS, LOGL_ERROR, "Unable to resolve NSEI %u " + LOGP(DNS, LOGL_ERROR, "Unable to resolve NSEI %u " "to NS-VC!\n", msgb_nsei(msg)); return -EINVAL; } if (!(nsvc->state & NSE_S_ALIVE)) { - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u is not alive, cannot send\n", + LOGP(DNS, LOGL_ERROR, "NSEI=%u is not alive, cannot send\n", nsvc->nsei); return -EBUSY; } if (nsvc->state & NSE_S_BLOCKED) { - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u is blocked, cannot send\n", + LOGP(DNS, LOGL_ERROR, "NSEI=%u is blocked, cannot send\n", nsvc->nsei); return -EBUSY; } nsh = (struct gprs_ns_hdr *) msgb_push(msg, sizeof(*nsh) + 3); if (!nsh) { - LOGP(DGPRS, LOGL_ERROR, "Not enough headroom for NS header\n"); + LOGP(DNS, LOGL_ERROR, "Not enough headroom for NS header\n"); return -EIO; } @@ -344,17 +344,17 @@ static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg) uint8_t cause; int rc; - DEBUGP(DGPRS, "NSEI=%u NS STATUS ", nsvc->nsei); + DEBUGP(DNS, "NSEI=%u NS STATUS ", nsvc->nsei); rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) { - DEBUGPC(DGPRS, "missing cause IE\n"); + DEBUGPC(DNS, "missing cause IE\n"); return -EINVAL; } cause = *TLVP_VAL(&tp, NS_IE_CAUSE); - DEBUGPC(DGPRS, "cause=%s\n", gprs_ns_cause_str(cause)); + DEBUGPC(DNS, "cause=%s\n", gprs_ns_cause_str(cause)); return 0; } @@ -374,7 +374,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) !TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) { /* FIXME: respond with NS_CAUSE_MISSING_ESSENT_IE */ - LOGP(DGPRS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); return -EINVAL; } @@ -382,7 +382,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); nsei = (uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI); - DEBUGP(DGPRS, "NSEI=%u NS RESET (NSVCI=%u, cause=%s)\n", + DEBUGP(DNS, "NSEI=%u NS RESET (NSVCI=%u, cause=%s)\n", nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause)); nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; @@ -409,13 +409,13 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, if (!nsvc) { /* Only the RESET procedure creates a new NSVC */ if (nsh->pdu_type != NS_PDUT_RESET) { - LOGP(DGPRS, LOGL_INFO, "Ignoring NS PDU type 0x%0x " + LOGP(DNS, LOGL_INFO, "Ignoring NS PDU type 0x%0x " "from %s for non-existing NS-VC\n", nsh->pdu_type, inet_ntoa(saddr->sin_addr)); //gprs_ns_tx_reset(nsvc, NS_CAUSE_NSVC_UNKNOWN); return -EIO; } - LOGP(DGPRS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", + LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); nsvc = nsvc_create(nsi, 0xffff); nsvc->ip.bts_addr = *saddr; @@ -450,7 +450,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, rc = gprs_ns_rx_reset(nsvc, msg); break; case NS_PDUT_RESET_ACK: - DEBUGP(DGPRS, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); + DEBUGP(DNS, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); /* mark remote NS-VC as blocked + active */ nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; if (nsvc->remote_end_is_sgsn) { @@ -465,29 +465,29 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, break; case NS_PDUT_UNBLOCK: /* Section 7.2: unblocking procedure */ - DEBUGP(DGPRS, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei); + DEBUGP(DNS, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei); nsvc->state &= ~NSE_S_BLOCKED; rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); break; case NS_PDUT_UNBLOCK_ACK: - DEBUGP(DGPRS, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei); + DEBUGP(DNS, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei); /* mark remote NS-VC as unblocked + active */ nsvc->remote_state = NSE_S_ALIVE; if (nsvc->remote_end_is_sgsn) nsvc->state = NSE_S_ALIVE; break; case NS_PDUT_BLOCK: - DEBUGP(DGPRS, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); + DEBUGP(DNS, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); nsvc->state |= NSE_S_BLOCKED; rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); break; case NS_PDUT_BLOCK_ACK: - DEBUGP(DGPRS, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei); + DEBUGP(DNS, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei); /* mark remote NS-VC as blocked + active */ nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; break; default: - DEBUGP(DGPRS, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n", + DEBUGP(DNS, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n", nsvc->nsei, nsh->pdu_type); rc = -EINVAL; break; @@ -533,7 +533,7 @@ static struct msgb *read_nsip_msg(struct bsc_fd *bfd, int *error, ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, (struct sockaddr *)saddr, &saddr_len); if (ret < 0) { - LOGP(DGPRS, LOGL_ERROR, "recv error %s during NSIP recv\n", + LOGP(DNS, LOGL_ERROR, "recv error %s during NSIP recv\n", strerror(errno)); msgb_free(msg); *error = ret; @@ -634,7 +634,7 @@ struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, /* Initiate a RESET procedure */ if (gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION) < 0) { - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n", + LOGP(DNS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n", nsei); } /* run a timer and re-transmit the reset request? */ -- cgit v1.2.3 From 834f26d62c55a7ba837ca370e7c60d0672aaead1 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 11 May 2010 06:20:54 +0200 Subject: [gprs] NS: Add signals in the event of BLOCK/UNBLOCK/RESET The signals will be sent upon reception of NS-BLOCK/UNBLOCK/RESET PDUs We also export functions to generate/send BLOCK/UNBLOCK and RESET. --- openbsc/src/gprs/gprs_ns.c | 77 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index be3d1d93..3fa2c016 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -128,6 +129,17 @@ static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) return nsvc; } +static void ns_dispatch_signal(struct nsvc *nsvc, unsigned int signal, + uint8_t cause) +{ + struct ns_signal_data nssd; + + nssd.nsvc = nsvc; + nssd.cause = cause; + + dispatch_signal(SS_NS, signal, &nssd); +} + /* Section 10.3.2, Table 13 */ static const struct value_string ns_cause_str[] = { { NS_CAUSE_TRANSIT_FAIL, "Transit network failure" }, @@ -183,7 +195,7 @@ static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type) return gprs_ns_tx(nsvc, msg); } -static int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) +int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) { struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); struct gprs_ns_hdr *nsh; @@ -204,6 +216,32 @@ static int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) } +int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + uint16_t nsvci = htons(nsvc->nsvci); + + if (!msg) + return -ENOMEM; + + /* be conservative and mark it as blocked even now! */ + nsvc->state |= NSE_S_BLOCKED; + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + nsh->pdu_type = NS_PDUT_BLOCK; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); + + return gprs_ns_tx(nsvc, msg); +} + +int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc) +{ + return gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK); +} + #define NS_ALIVE_RETRIES 10 /* after 3 failed retransmit we declare BTS as dead */ static const uint8_t timer_mode_tout[_NSVC_TIMER_NR] = { @@ -393,9 +431,41 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) /* start the test procedure */ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + /* inform interested parties about the fact that this NSVC + * has received RESET */ + ns_dispatch_signal(nsvc, S_NS_RESET, cause); + return gprs_ns_tx_reset_ack(nsvc); } +static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct tlv_parsed tp; + uint8_t *cause; + int rc; + + DEBUGP(DNS, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); + + nsvc->state |= NSE_S_BLOCKED; + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || + !TLVP_PRESENT(&tp, NS_IE_VCI)) { + /* FIXME: respond with NS_CAUSE_MISSING_ESSENT_IE */ + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + return -EINVAL; + } + + cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); + //nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); + + ns_dispatch_signal(nsvc, S_NS_BLOCK, cause); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_BLOCK_ACK); +} + /* main entry point, here incoming NS frames enter */ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, struct sockaddr_in *saddr) @@ -467,6 +537,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, /* Section 7.2: unblocking procedure */ DEBUGP(DNS, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei); nsvc->state &= ~NSE_S_BLOCKED; + ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0); rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); break; case NS_PDUT_UNBLOCK_ACK: @@ -477,9 +548,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, nsvc->state = NSE_S_ALIVE; break; case NS_PDUT_BLOCK: - DEBUGP(DNS, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); - nsvc->state |= NSE_S_BLOCKED; - rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); + rc = gprs_ns_rx_block(nsvc, msg); break; case NS_PDUT_BLOCK_ACK: DEBUGP(DNS, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei); -- cgit v1.2.3 From 2fc725de723b534a8a9c031ef12429ffa6bb092f Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 11 May 2010 10:15:26 +0200 Subject: [gprs] NS: cleanup / fix compiler warnings --- openbsc/src/gprs/gprs_ns.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 3fa2c016..69c96ca3 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -129,7 +129,7 @@ static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) return nsvc; } -static void ns_dispatch_signal(struct nsvc *nsvc, unsigned int signal, +static void ns_dispatch_signal(struct gprs_nsvc *nsvc, unsigned int signal, uint8_t cause) { struct ns_signal_data nssd; @@ -291,6 +291,8 @@ static void gprs_ns_timer_cb(void *data) gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION); nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); break; + case _NSVC_TIMER_NR: + break; } } @@ -433,7 +435,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) /* inform interested parties about the fact that this NSVC * has received RESET */ - ns_dispatch_signal(nsvc, S_NS_RESET, cause); + ns_dispatch_signal(nsvc, S_NS_RESET, *cause); return gprs_ns_tx_reset_ack(nsvc); } @@ -461,7 +463,7 @@ static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); //nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); - ns_dispatch_signal(nsvc, S_NS_BLOCK, cause); + ns_dispatch_signal(nsvc, S_NS_BLOCK, *cause); return gprs_ns_tx_simple(nsvc, NS_PDUT_BLOCK_ACK); } -- cgit v1.2.3 From e8b9ca297259c5ac9918710d5b89e585ef28acf3 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 11 May 2010 18:18:31 +0200 Subject: [gprs] NS: Make sure we include "Rx" in the log statement for NS RESET --- openbsc/src/gprs/gprs_ns.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 69c96ca3..bd9791af 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -422,10 +422,14 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); nsei = (uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI); - DEBUGP(DNS, "NSEI=%u NS RESET (NSVCI=%u, cause=%s)\n", + DEBUGP(DNS, "NSEI=%u Rx NS RESET (NSVCI=%u, cause=%s)\n", nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause)); nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + /* FIXME: Check if we have an existing peer with this NSEI/NSVCI + * and remove it, as our BSS may just have changed its source IP + * address */ + nsvc->nsei = ntohs(*nsei); nsvc->nsvci = ntohs(*nsvci); -- cgit v1.2.3 From 9a39293ced5f44f8bad243f50b6cb8af4414d91f Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 11 May 2010 18:30:37 +0200 Subject: [gprs] gb-proxy: We might receive a NS-RESET for a NS-VC that we already know In this case, don't blindly allocate a new NS-VC but rather use the NSEI to lookup the 'struct gprs_nsvc' for it. --- openbsc/src/gprs/gprs_ns.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index bd9791af..3d05dd0f 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -483,6 +483,8 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, /* look up the NSVC based on source address */ nsvc = nsvc_by_rem_addr(nsi, saddr); if (!nsvc) { + struct tlv_parsed tp; + uint16_t nsei; /* Only the RESET procedure creates a new NSVC */ if (nsh->pdu_type != NS_PDUT_RESET) { LOGP(DNS, LOGL_INFO, "Ignoring NS PDU type 0x%0x " @@ -491,10 +493,25 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, //gprs_ns_tx_reset(nsvc, NS_CAUSE_NSVC_UNKNOWN); return -EIO; } - LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", - inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); - nsvc = nsvc_create(nsi, 0xffff); - nsvc->ip.bts_addr = *saddr; + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, + msgb_l2len(msg), 0, 0); + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || + !TLVP_PRESENT(&tp, NS_IE_VCI) || + !TLVP_PRESENT(&tp, NS_IE_NSEI)) { + /* FIXME: respond with NS_CAUSE_MISSING_ESSENT_IE */ + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + return -EINVAL; + } + nsei = ntohs(*(uint16_t *)TLVP_VAL(&tp, NS_IE_NSEI)); + /* Check if we already know this NSEI, the remote end might + * simply have changed addresses, or it is a SGSN */ + nsvc = nsvc_by_nsei(nsi, nsei); + if (!nsvc) { + LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", + inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); + nsvc = nsvc_create(nsi, 0xffff); + nsvc->ip.bts_addr = *saddr; + } } else msgb_nsei(msg) = nsvc->nsei; -- cgit v1.2.3 From bba902db196e4e947b786022e9c19aa3f1ae195e Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 11 May 2010 18:38:36 +0200 Subject: [gprs] NS: include port number in log statement --- openbsc/src/gprs/gprs_ns.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 3d05dd0f..9c0c4ec9 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -488,8 +488,9 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, /* Only the RESET procedure creates a new NSVC */ if (nsh->pdu_type != NS_PDUT_RESET) { LOGP(DNS, LOGL_INFO, "Ignoring NS PDU type 0x%0x " - "from %s for non-existing NS-VC\n", - nsh->pdu_type, inet_ntoa(saddr->sin_addr)); + "from %s:%u for non-existing NS-VC\n", + nsh->pdu_type, inet_ntoa(saddr->sin_addr), + ntohs(saddr->sin_port)); //gprs_ns_tx_reset(nsvc, NS_CAUSE_NSVC_UNKNOWN); return -EIO; } -- cgit v1.2.3 From 2b7d4658fb603c910f77feceea85392ed0f31e51 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 11 May 2010 18:40:45 +0200 Subject: [gprs] NS: update the remote peer IP addr/port on NS RESET --- openbsc/src/gprs/gprs_ns.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 9c0c4ec9..745a0b57 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -511,8 +511,9 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); nsvc = nsvc_create(nsi, 0xffff); - nsvc->ip.bts_addr = *saddr; } + /* Update the remote peer IP address/port */ + nsvc->ip.bts_addr = *saddr; } else msgb_nsei(msg) = nsvc->nsei; -- cgit v1.2.3 From e4ecc8c81e0333506e1ddee18ac82321d8950e81 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 00:16:57 +0200 Subject: [gprs] NS: elevate events from LOGL_DEBUG to LOGL_INFO --- openbsc/src/gprs/gprs_ns.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 745a0b57..fdefa653 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -272,10 +272,11 @@ static void gprs_ns_timer_cb(void *data) if (nsvc->alive_retries > NS_ALIVE_RETRIES) { /* mark as dead and blocked */ nsvc->state = NSE_S_BLOCKED; - DEBUGP(DNS, "NSEI=%u Tns-alive expired more then " + LOGP(DNS, LOGL_NOTICE, + "NSEI=%u Tns-alive expired more then " "%u times, blocking NS-VC\n", nsvc->nsei, NS_ALIVE_RETRIES); - /* FIXME: inform higher layers */ + ns_dispatch_signal(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED); return; } nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); @@ -313,7 +314,7 @@ static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) nsh->pdu_type = NS_PDUT_RESET_ACK; - DEBUGP(DNS, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n", + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n", nsvc->nsei, nsvc->nsvci); msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); @@ -384,17 +385,17 @@ static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg) uint8_t cause; int rc; - DEBUGP(DNS, "NSEI=%u NS STATUS ", nsvc->nsei); + LOGP(DNS, LOGL_INFO, "NSEI=%u NS STATUS ", nsvc->nsei); rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) { - DEBUGPC(DNS, "missing cause IE\n"); + LOGPC(DNS, LOGL_INFO, "missing cause IE\n"); return -EINVAL; } cause = *TLVP_VAL(&tp, NS_IE_CAUSE); - DEBUGPC(DNS, "cause=%s\n", gprs_ns_cause_str(cause)); + LOGPC(DNS, LOGL_INFO, "cause=%s\n", gprs_ns_cause_str(cause)); return 0; } @@ -422,7 +423,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); nsei = (uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI); - DEBUGP(DNS, "NSEI=%u Rx NS RESET (NSVCI=%u, cause=%s)\n", + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET (NSVCI=%u, cause=%s)\n", nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause)); nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; @@ -451,7 +452,7 @@ static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) uint8_t *cause; int rc; - DEBUGP(DNS, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); nsvc->state |= NSE_S_BLOCKED; @@ -545,7 +546,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, rc = gprs_ns_rx_reset(nsvc, msg); break; case NS_PDUT_RESET_ACK: - DEBUGP(DNS, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); /* mark remote NS-VC as blocked + active */ nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; if (nsvc->remote_end_is_sgsn) { @@ -560,13 +561,13 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, break; case NS_PDUT_UNBLOCK: /* Section 7.2: unblocking procedure */ - DEBUGP(DNS, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei); + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei); nsvc->state &= ~NSE_S_BLOCKED; ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0); rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); break; case NS_PDUT_UNBLOCK_ACK: - DEBUGP(DNS, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei); + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei); /* mark remote NS-VC as unblocked + active */ nsvc->remote_state = NSE_S_ALIVE; if (nsvc->remote_end_is_sgsn) @@ -576,12 +577,12 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, rc = gprs_ns_rx_block(nsvc, msg); break; case NS_PDUT_BLOCK_ACK: - DEBUGP(DNS, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei); + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei); /* mark remote NS-VC as blocked + active */ nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; break; default: - DEBUGP(DNS, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n", + LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n", nsvc->nsei, nsh->pdu_type); rc = -EINVAL; break; -- cgit v1.2.3 From c1402a68c55ea8c30b2aa9db7ae16e5df0820948 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 11:48:44 +0200 Subject: [gprs] NS: more state transitions, error reporting via Tx STATUS PDU --- openbsc/src/gprs/gprs_ns.c | 80 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 16 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index fdefa653..9cb33005 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -216,6 +216,49 @@ int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) } +int gprs_ns_tx_status(struct gprs_nsvc *nsvc, uint8_t cause, + uint16_t bvci, struct msgb *orig_msg) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + uint16_t nsvci = htons(nsvc->nsvci); + + bvci = htons(bvci); + + if (!msg) + return -ENOMEM; + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + nsh->pdu_type = NS_PDUT_STATUS; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + + /* Section 9.2.7.1: Static conditions for NS-VCI */ + if (cause == NS_CAUSE_NSVC_BLOCKED || + cause == NS_CAUSE_NSVC_UNKNOWN) + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); + + /* Section 9.2.7.2: Static conditions for NS PDU */ + switch (cause) { + case NS_CAUSE_SEM_INCORR_PDU: + case NS_CAUSE_PDU_INCOMP_PSTATE: + case NS_CAUSE_PROTO_ERR_UNSPEC: + case NS_CAUSE_INVAL_ESSENT_IE: + case NS_CAUSE_MISSING_ESSENT_IE: + msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg), + orig_msg->l2h); + break; + default: + break; + } + + /* Section 9.2.7.3: Static conditions for BVCI */ + if (cause == NS_CAUSE_BVCI_UNKNOWN) + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci); + + return gprs_ns_tx(nsvc, msg); +} + int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause) { struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); @@ -290,6 +333,7 @@ static void gprs_ns_timer_cb(void *data) case NSVC_TIMER_TNS_RESET: /* Chapter 7.3: Re-send the RESET */ gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION); + /* Re-start Tns-reset timer */ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); break; case _NSVC_TIMER_NR: @@ -368,6 +412,10 @@ static int gprs_ns_rx_unitdata(struct gprs_nsvc *nsvc, struct msgb *msg) struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; uint16_t bvci; + if (nsvc->state & NSE_S_BLOCKED) + return gprs_ns_tx_status(nsvc, NS_CAUSE_NSVC_BLOCKED, + 0, msg); + /* spare octet in data[0] */ bvci = nsh->data[1] << 8 | nsh->data[2]; msgb_bssgph(msg) = &nsh->data[3]; @@ -414,8 +462,8 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || !TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) { - /* FIXME: respond with NS_CAUSE_MISSING_ESSENT_IE */ LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); return -EINVAL; } @@ -426,15 +474,12 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET (NSVCI=%u, cause=%s)\n", nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause)); + /* Mark NS-VC as blocked and alive */ nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; - /* FIXME: Check if we have an existing peer with this NSEI/NSVCI - * and remove it, as our BSS may just have changed its source IP - * address */ nsvc->nsei = ntohs(*nsei); nsvc->nsvci = ntohs(*nsvci); - /* mark the NS-VC as blocked and alive */ /* start the test procedure */ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); @@ -460,8 +505,8 @@ static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || !TLVP_PRESENT(&tp, NS_IE_VCI)) { - /* FIXME: respond with NS_CAUSE_MISSING_ESSENT_IE */ LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); return -EINVAL; } @@ -492,6 +537,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, "from %s:%u for non-existing NS-VC\n", nsh->pdu_type, inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); + /* FIXME: send STATUS (but we have no NSVC!) */ //gprs_ns_tx_reset(nsvc, NS_CAUSE_NSVC_UNKNOWN); return -EIO; } @@ -500,8 +546,9 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || !TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) { - /* FIXME: respond with NS_CAUSE_MISSING_ESSENT_IE */ LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, + msg); return -EINVAL; } nsei = ntohs(*(uint16_t *)TLVP_VAL(&tp, NS_IE_NSEI)); @@ -532,7 +579,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, if (nsvc->remote_end_is_sgsn) { /* FIXME: this should be one level higher */ if (nsvc->state & NSE_S_BLOCKED) - rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK); + rc = gprs_ns_tx_unblock(nsvc); } break; case NS_PDUT_UNITDATA: @@ -547,16 +594,15 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, break; case NS_PDUT_RESET_ACK: LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); - /* mark remote NS-VC as blocked + active */ + /* mark NS-VC as blocked + active */ + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; if (nsvc->remote_end_is_sgsn) { /* stop RESET timer */ bsc_del_timer(&nsvc->timer); - /* send ALIVE PDU */ + /* Initiate TEST proc.: Send ALIVE and start timer */ rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); - /* mark local state as BLOCKED + ALIVE */ - nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; } break; case NS_PDUT_UNBLOCK: @@ -568,10 +614,9 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, break; case NS_PDUT_UNBLOCK_ACK: LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei); - /* mark remote NS-VC as unblocked + active */ + /* mark NS-VC as unblocked + active */ + nsvc->state = NSE_S_ALIVE; nsvc->remote_state = NSE_S_ALIVE; - if (nsvc->remote_end_is_sgsn) - nsvc->state = NSE_S_ALIVE; break; case NS_PDUT_BLOCK: rc = gprs_ns_rx_block(nsvc, msg); @@ -728,11 +773,14 @@ struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, nsvc->remote_end_is_sgsn = 1; /* Initiate a RESET procedure */ + /* Mark NS-VC locally as blocked and dead */ + nsvc->state = NSE_S_BLOCKED; + /* Send NS-RESET PDU */ if (gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION) < 0) { LOGP(DNS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n", nsei); } - /* run a timer and re-transmit the reset request? */ + /* Start Tns-reset */ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); return nsvc; -- cgit v1.2.3 From 38407efdf88992e11c06659eb3f15d3142e1726f Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 11:56:39 +0000 Subject: NS: don't memcmp sockaddr_in but compare ip and port individually this seems to work more portably (ppc/32bit big endian) --- openbsc/src/gprs/gprs_ns.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 9cb33005..a166956b 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -104,7 +104,9 @@ static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi, { struct gprs_nsvc *nsvc; llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { - if (!memcmp(&nsvc->ip.bts_addr, sin, sizeof(*sin))) + if (nsvc->ip.bts_addr.sin_addr.s_addr == + sin->sin_addr.s_addr && + nsvc->ip.bts_addr.sin_port == sin->sin_port) return nsvc; } return NULL; @@ -764,10 +766,9 @@ struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, struct gprs_nsvc *nsvc; nsvc = nsvc_by_rem_addr(nsi, dest); - if (!nsvc) { + if (!nsvc) nsvc = nsvc_create(nsi, nsvci); - nsvc->ip.bts_addr = *dest; - } + nsvc->ip.bts_addr = *dest; nsvc->nsei = nsei; nsvc->nsvci = nsvci; nsvc->remote_end_is_sgsn = 1; -- cgit v1.2.3 From 6ecaa3da2b2ff5dda53840476a792e12ce99d372 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 12:01:33 +0000 Subject: NS: More INFO messages about what we actually transmit --- openbsc/src/gprs/gprs_ns.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index a166956b..e93bd43f 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -207,6 +207,9 @@ int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) if (!msg) return -ENOMEM; + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n", + nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); nsh->pdu_type = NS_PDUT_RESET; @@ -230,6 +233,9 @@ int gprs_ns_tx_status(struct gprs_nsvc *nsvc, uint8_t cause, if (!msg) return -ENOMEM; + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n", + nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); nsh->pdu_type = NS_PDUT_STATUS; @@ -270,6 +276,9 @@ int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause) if (!msg) return -ENOMEM; + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK (NSVCI=%u, cause=%s)\n", + nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); + /* be conservative and mark it as blocked even now! */ nsvc->state |= NSE_S_BLOCKED; @@ -284,6 +293,10 @@ int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause) int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc) { + + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + return gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK); } -- cgit v1.2.3 From b983c3104dd3e187a9d7a424b1f70ef6ef8f615e Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 12:11:45 +0000 Subject: NS: Add more LOG_DEBGU messages and retransmit NS-ALIVE --- openbsc/src/gprs/gprs_ns.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index e93bd43f..19653f6a 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -293,13 +293,28 @@ int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause) int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc) { - LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n", nsvc->nsei, nsvc->nsvci); return gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK); } +int gprs_ns_tx_alive(struct gprs_nsvc *nsvc) +{ + LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); +} + +int gprs_ns_tx_alive_ack(struct gprs_nsvc *nsvc) +{ + LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE_ACK); +} + #define NS_ALIVE_RETRIES 10 /* after 3 failed retransmit we declare BTS as dead */ static const uint8_t timer_mode_tout[_NSVC_TIMER_NR] = { @@ -337,12 +352,16 @@ static void gprs_ns_timer_cb(void *data) ns_dispatch_signal(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED); return; } + /* Tns-test case: send NS-ALIVE PDU */ + gprs_ns_tx_alive(nsvc); + /* start Tns-alive timer */ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); break; case NSVC_TIMER_TNS_TEST: /* Tns-test case: send NS-ALIVE PDU */ - gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); - /* start Tns-alive timer */ + gprs_ns_tx_alive(nsvc); + /* start Tns-alive timer (transition into faster + * alive retransmissions) */ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); break; case NSVC_TIMER_TNS_RESET: @@ -584,7 +603,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, case NS_PDUT_ALIVE: /* remote end inquires whether we're still alive, * we need to respond with ALIVE_ACK */ - rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE_ACK); + rc = gprs_ns_tx_alive_ack(nsvc); break; case NS_PDUT_ALIVE_ACK: /* stop Tns-alive */ -- cgit v1.2.3 From 7c24b9edbc784a225ed0ce8e688f2c1e8511c5da Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 12:21:52 +0000 Subject: NS: Debug NS timer expiry --- openbsc/src/gprs/gprs_ns.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 19653f6a..0ef0c3fc 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -323,10 +323,19 @@ static const uint8_t timer_mode_tout[_NSVC_TIMER_NR] = { [NSVC_TIMER_TNS_TEST] = 30, }; +static const struct value_string timer_mode_strs[] = { + { NSVC_TIMER_TNS_RESET, "tns-reset" }, + { NSVC_TIMER_TNS_ALIVE, "tns-alive" }, + { NSVC_TIMER_TNS_TEST, "tns-test" }, + { 0, NULL } +}; + static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode) { - nsvc->alive_retries = 0; - + DEBUGP(DNS, "NSVC=%u Starting timer in mode %s (%u seconds)\n", + nsvc->nsvci, get_value_string(timer_mode_strs, mode), + timer_mode_tout[mode]); + if (bsc_timer_pending(&nsvc->timer)) bsc_del_timer(&nsvc->timer); @@ -338,6 +347,10 @@ static void gprs_ns_timer_cb(void *data) { struct gprs_nsvc *nsvc = data; + DEBUGP(DNS, "NSVC=%u Timer expired in mode %s (%u seconds)\n", + nsvc->nsvci, get_value_string(timer_mode_strs, nsvc->timer_mode), + timer_mode_tout[nsvc->timer_mode]); + switch (nsvc->timer_mode) { case NSVC_TIMER_TNS_ALIVE: /* Tns-alive case: we expired without response ! */ @@ -362,6 +375,7 @@ static void gprs_ns_timer_cb(void *data) gprs_ns_tx_alive(nsvc); /* start Tns-alive timer (transition into faster * alive retransmissions) */ + nsvc->alive_retries = 0; nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); break; case NSVC_TIMER_TNS_RESET: -- cgit v1.2.3 From 4941b35be5b827b859a773cfe2b18542515b094d Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 13:28:25 +0000 Subject: NS / GB Proxy: Add Signal in case Tns-Alive expires too often The Gb Proxy can then restart the RESET procedure. --- openbsc/src/gprs/gprs_ns.c | 1 + 1 file changed, 1 insertion(+) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 0ef0c3fc..fd0b27a6 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -362,6 +362,7 @@ static void gprs_ns_timer_cb(void *data) "NSEI=%u Tns-alive expired more then " "%u times, blocking NS-VC\n", nsvc->nsei, NS_ALIVE_RETRIES); + ns_dispatch_signal(nsvc, S_NS_ALIVE_EXP, 0); ns_dispatch_signal(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED); return; } -- cgit v1.2.3 From 803647e4afbf07e42fc23f7b16bb1640fe43b249 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 13:51:08 +0000 Subject: NS: Send STATUS or RESET when receiving NS_ALIVE on unknown NSVC --- openbsc/src/gprs/gprs_ns.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index fd0b27a6..13ea92dd 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -582,13 +582,25 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, uint16_t nsei; /* Only the RESET procedure creates a new NSVC */ if (nsh->pdu_type != NS_PDUT_RESET) { + struct gprs_nsvc fake_nsvc; LOGP(DNS, LOGL_INFO, "Ignoring NS PDU type 0x%0x " "from %s:%u for non-existing NS-VC\n", nsh->pdu_type, inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); - /* FIXME: send STATUS (but we have no NSVC!) */ - //gprs_ns_tx_reset(nsvc, NS_CAUSE_NSVC_UNKNOWN); - return -EIO; + /* Since we have no NSVC, we have to create a fake */ + fake_nsvc.nsvci = fake_nsvc.nsei = 0; + fake_nsvc.nsi = nsi; + fake_nsvc.ip.bts_addr = *saddr; + fake_nsvc.state = NSE_S_ALIVE; +#if 0 + return gprs_ns_tx_status(&fake_nsvc, + NS_CAUSE_PDU_INCOMP_PSTATE, 0, + msg); +#else + /* BS+ Gb implementation ignores STATUS, so we try + * our luck with a RESET incompatible with the spec */ + return gprs_ns_tx_simple(&fake_nsvc, NS_PDUT_RESET); +#endif } rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); @@ -785,8 +797,6 @@ static int nsip_fd_cb(struct bsc_fd *bfd, unsigned int what) return rc; } - -/* FIXME: this is currently in input/ipaccess.c */ extern int make_sock(struct bsc_fd *bfd, int proto, uint16_t port, int (*cb)(struct bsc_fd *fd, unsigned int what)); -- cgit v1.2.3 From 4e187c6dbb9c428ca45f8043f51925a2bde4b520 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 13:55:36 +0000 Subject: Display NSEI instead of NSVCI for all debug msgs --- openbsc/src/gprs/gprs_ns.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 13ea92dd..8cb26a63 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -332,8 +332,8 @@ static const struct value_string timer_mode_strs[] = { static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode) { - DEBUGP(DNS, "NSVC=%u Starting timer in mode %s (%u seconds)\n", - nsvc->nsvci, get_value_string(timer_mode_strs, mode), + DEBUGP(DNS, "NSEI=%u Starting timer in mode %s (%u seconds)\n", + nsvc->nsei, get_value_string(timer_mode_strs, mode), timer_mode_tout[mode]); if (bsc_timer_pending(&nsvc->timer)) @@ -347,8 +347,8 @@ static void gprs_ns_timer_cb(void *data) { struct gprs_nsvc *nsvc = data; - DEBUGP(DNS, "NSVC=%u Timer expired in mode %s (%u seconds)\n", - nsvc->nsvci, get_value_string(timer_mode_strs, nsvc->timer_mode), + DEBUGP(DNS, "NSEI=%u Timer expired in mode %s (%u seconds)\n", + nsvc->nsei, get_value_string(timer_mode_strs, nsvc->timer_mode), timer_mode_tout[nsvc->timer_mode]); switch (nsvc->timer_mode) { -- cgit v1.2.3 From f88dc001821bb98986668b9daee98d9cb5b1cdd2 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 14:18:46 +0000 Subject: NS: Send UNBLOCK signal when we get UNBLOCK_ACK from peer --- openbsc/src/gprs/gprs_ns.c | 1 + 1 file changed, 1 insertion(+) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 8cb26a63..3d672fb8 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -678,6 +678,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, /* mark NS-VC as unblocked + active */ nsvc->state = NSE_S_ALIVE; nsvc->remote_state = NSE_S_ALIVE; + ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0); break; case NS_PDUT_BLOCK: rc = gprs_ns_rx_block(nsvc, msg); -- cgit v1.2.3 From 2bffac599f4a93a3113a7260955ac3ec1cd4b39c Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 12 May 2010 15:55:23 +0000 Subject: NS: Add support for persistent NS-VC configuration With persistent NS-VC configuration (configured through VTY), we can respond properly to BSS with a somewhat strange NS implementation Such as the BSplus. It enables us to respond with a proper NS-RESET (including NSVCI/NSEI) when receiving a NS-ALIVE or other PDU for a BLOCKED/DEAD NS-VC after our end of the connection is rebooted. --- openbsc/src/gprs/gprs_ns.c | 225 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 216 insertions(+), 9 deletions(-) (limited to 'openbsc/src/gprs/gprs_ns.c') diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 3d672fb8..ed642103 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -131,6 +131,14 @@ static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) return nsvc; } +static void nsvc_delete(struct gprs_nsvc *nsvc) +{ + if (bsc_timer_pending(&nsvc->timer)) + bsc_del_timer(&nsvc->timer); + llist_del(&nsvc->list); + talloc_free(nsvc); +} + static void ns_dispatch_signal(struct gprs_nsvc *nsvc, unsigned int signal, uint8_t cause) { @@ -592,15 +600,9 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, fake_nsvc.nsi = nsi; fake_nsvc.ip.bts_addr = *saddr; fake_nsvc.state = NSE_S_ALIVE; -#if 0 return gprs_ns_tx_status(&fake_nsvc, NS_CAUSE_PDU_INCOMP_PSTATE, 0, msg); -#else - /* BS+ Gb implementation ignores STATUS, so we try - * our luck with a RESET incompatible with the spec */ - return gprs_ns_tx_simple(&fake_nsvc, NS_PDUT_RESET); -#endif } rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); @@ -628,9 +630,14 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, switch (nsh->pdu_type) { case NS_PDUT_ALIVE: - /* remote end inquires whether we're still alive, - * we need to respond with ALIVE_ACK */ - rc = gprs_ns_tx_alive_ack(nsvc); + /* If we're dead and blocked and suddenly receive a + * NS-ALIVE out of the blue, we might have been re-started + * and should send a NS-RESET to make sure everything recovers + * fine. */ + if (nsvc->state == NSE_S_BLOCKED) + rc = gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE); + else + rc = gprs_ns_tx_alive_ack(nsvc); break; case NS_PDUT_ALIVE_ACK: /* stop Tns-alive */ @@ -844,3 +851,203 @@ struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, return nsvc; } + +#include +#include + +static struct gprs_ns_inst *vty_nsi = NULL; + +static struct cmd_node ns_node = { + NS_NODE, + "%s(ns)#", + 1, +}; + +static int config_write_ns(struct vty *vty) +{ + struct gprs_nsvc *nsvc; + + vty_out(vty, "ns%s", VTY_NEWLINE); + + llist_for_each_entry(nsvc, &vty_nsi->gprs_nsvcs, list) { + if (!nsvc->persistent) + continue; + vty_out(vty, " nse %u nsvci %u%s", + nsvc->nsei, nsvc->nsvci, VTY_NEWLINE); + vty_out(vty, " nse %u remote-role %s%s", + nsvc->nsei, nsvc->remote_end_is_sgsn ? "sgsn" : "bss", + VTY_NEWLINE); + if (nsvc->nsi->ll == GPRS_NS_LL_UDP) { + vty_out(vty, " nse %u remote-ip %s%s", + nsvc->nsei, + inet_ntoa(nsvc->ip.bts_addr.sin_addr), + VTY_NEWLINE); + vty_out(vty, " nse %u remote-port %u%s", + nsvc->nsei, ntohs(nsvc->ip.bts_addr.sin_port), + VTY_NEWLINE);