Change in libosmocore[master]: Introduce info / features negotiation helper functions

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Vadim Yanitskiy gerrit-no-reply at lists.osmocom.org
Wed Jul 18 02:02:46 UTC 2018


Vadim Yanitskiy has uploaded this change for review. ( https://gerrit.osmocom.org/10034


Change subject: Introduce info / features negotiation helper functions
......................................................................

Introduce info / features negotiation helper functions

In some projects, such as OsmocomBB and SIMTrace, where the host
software needs to communicate with different HW / SW back-ends
(e.g. OsmocomBB/mobile and Calypso firmware), it is important
to have a possibility to negotiate some information between
the both sides, for example, a list of supported features,
or some meta-information about a hardware back-end.

This change introduces the new API exactly for that, and can
be used in order to avoid code duplication, and implementing
the wheel again and again.

At the moment, there are two kinds of information that can be
negotiated:

  - TLV-based text chunks,
  - feature flags.

First kind of info can be used to describe some meta-information,
for example, HW board name, FW or SW version. Also, it can be
used to describe some non-binary properties, for example,
a preferred TCH frame format.

The feature flags can be used to indicate that one or more
features from a set shared between the both sides is/are
supported. If particular flag is preset in the encoded
message, the associated feature is supported, otherwise
it is not supported, or not implemented.

Basic usage examples can be found in implementation
of the unit test: tests/nego/nego_test.c.

Change-Id: I6fd5b6be6755d4aa735a510e95459180225389ba
---
M include/Makefile.am
A include/osmocom/core/negotiation.h
M src/Makefile.am
A src/negotiation.c
M tests/Makefile.am
A tests/nego/nego_test.c
A tests/nego/nego_test.ok
M tests/testsuite.at
8 files changed, 428 insertions(+), 3 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/34/10034/1

diff --git a/include/Makefile.am b/include/Makefile.am
index 38ba14c..158a1b8 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -51,6 +51,7 @@
                        osmocom/core/timer_compat.h \
                        osmocom/core/utils.h \
                        osmocom/core/write_queue.h \
+                       osmocom/core/negotiation.h \
                        osmocom/crypt/auth.h \
                        osmocom/crypt/gprs_cipher.h \
 		       osmocom/ctrl/control_cmd.h \
diff --git a/include/osmocom/core/negotiation.h b/include/osmocom/core/negotiation.h
new file mode 100644
index 0000000..86096f0
--- /dev/null
+++ b/include/osmocom/core/negotiation.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+int osmo_nego_enc_info(struct msgb *msg, const struct value_string *info);
+const char *osmo_nego_get_info(const uint8_t *buf, size_t buf_len, uint8_t info_tag);
+
+void osmo_nego_add_feature(struct msgb *msg, uint8_t feature);
+bool osmo_nego_check_feature(const uint8_t *buf, size_t buf_len, uint8_t feature);
diff --git a/src/Makefile.am b/src/Makefile.am
index 45fb89d..91cdefb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,7 +23,7 @@
 			 loggingrb.c crc8gen.c crc16gen.c crc32gen.c crc64gen.c \
 			 macaddr.c stat_item.c stats.c stats_statsd.c prim.c \
 			 conv_acc.c conv_acc_generic.c sercomm.c prbs.c \
-			 isdnhdlc.c
+			 isdnhdlc.c negotiation.c
 
 if HAVE_SSSE3
 libosmocore_la_SOURCES += conv_acc_sse.c
diff --git a/src/negotiation.c b/src/negotiation.c
new file mode 100644
index 0000000..5aa5d34
--- /dev/null
+++ b/src/negotiation.c
@@ -0,0 +1,138 @@
+/*! \file negotiation.c
+ * Helper functions for info / feature negotiation. */
+/*
+ * (C) 2018 by Vadim Yanitskiy <axilirator at gmail.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/tlv.h>
+
+/*! Encode given list of information into a TLV-based message buffer
+ *
+ *  The given list of information is represented by \ref value_string
+ *  structure, where value defines a kind of information (e.g. firmware
+ *  version, board name), and string in its turn defines the information
+ *  itself (e.g. '0.8.1.28-91e7b', 'AT91SAM7S').
+ *
+ *  Limitations: Tag and Length values occupy one byte each, so it's
+ *  possible to encode up to 256 unique information chunks, up to
+ *  256 bytes long each. This should be enough for the most cases.
+ *  Exceeding T and V values are not permitted.
+ *
+ *  Note: strings are encoded with '\0'!
+ *
+ *  \param[out] msg caller allocated message buffer
+ *  \param[in] info list of information to be encoded
+ *  \returns        how many info chunks were encoded
+ */
+int osmo_nego_enc_info(struct msgb *msg, const struct value_string *info)
+{
+	size_t len;
+	int i;
+
+	/* Iterate over a given info list */
+	for (i = 0;; i++) {
+		if (info[i].value == 0 && info[i].str == NULL)
+			break;
+
+		/* Encoding limitations */
+		OSMO_ASSERT(info[i].value <= 0xff);
+		OSMO_ASSERT(info[i].str != NULL);
+
+		/* Strings are encoded with '\0' */
+		len = strlen(info[i].str) + 1;
+		OSMO_ASSERT(len <= 0xff);
+
+		msgb_tlv_put(msg, info[i].value, len,
+			(const uint8_t *) info[i].str);
+	}
+
+	return i;
+}
+
+/*! Get info chunk corresponding to a given tag
+ *
+ *  \param[in] buf      TLV-encoded buffer to be parsed
+ *  \param[in] buf_len  TLV-encoded buffer length
+ *  \param[in] info_tag tag of required info
+ *  \returns            pointer to info string or NULL
+ */
+const char *osmo_nego_get_info(const uint8_t *buf, size_t buf_len, uint8_t info_tag)
+{
+	const uint8_t *pos = buf;
+	uint8_t tag, len;
+
+	while ((pos + 2) < (buf + buf_len)) {
+		tag = pos[0];
+		len = pos[1];
+
+		/* Prevent string out of bounds */
+		if ((pos + 2 + len) > (buf + buf_len))
+			break;
+
+		/* Match info tag */
+		if (tag == info_tag)
+			return (char *)(pos + 2);
+
+		/* Seek the pointer */
+		pos += len + 2;
+	}
+
+	return NULL;
+}
+
+/*! Append a new feature to a given list of features
+ *
+ *  Limitations: up to 256 features can be encoded.
+ *
+ *  \param[out] msg    caller allocated message buffer
+ *  \param[in] feature a new feature to be added
+ */
+void osmo_nego_add_feature(struct msgb *msg, uint8_t feature)
+{
+	uint8_t *byte = msgb_put(msg, 1);
+	*byte = feature;
+}
+
+/*! Append a new feature to a given list of features
+ *
+ *  \param[in] buf      feature list to be parsed
+ *  \param[in] buf_len  feature list length
+ *  \param[in] feature  the feature to be checked
+ *  \returns            true if the feature is preset,
+ *                      false otherwise
+ */
+bool osmo_nego_check_feature(const uint8_t *buf, size_t buf_len, uint8_t feature)
+{
+	size_t i;
+
+	for (i = 0; i < buf_len; i++)
+		if (buf[i] == feature)
+			return true;
+
+	return false;
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index db4e520..9e75dcf 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -23,7 +23,8 @@
 		 coding/coding_test conv/conv_gsm0503_test		\
 		 abis/abis_test endian/endian_test sercomm/sercomm_test	\
 		 prbs/prbs_test gsm23003/gsm23003_test 			\
-		 codec/codec_ecu_fr_test timer/clk_override_test
+		 codec/codec_ecu_fr_test timer/clk_override_test	\
+		 nego/nego_test
 
 if ENABLE_MSGFILE
 check_PROGRAMS += msgfile/msgfile_test
@@ -194,6 +195,9 @@
 gsm23003_gsm23003_test_SOURCES = gsm23003/gsm23003_test.c
 gsm23003_gsm23003_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
 
+nego_nego_test_SOURCES = nego/nego_test.c
+nego_nego_test_LDADD = $(LDADD)
+
 # The `:;' works around a Bash 3.2 bug when the output is not writeable.
 $(srcdir)/package.m4: $(top_srcdir)/configure.ac
 	:;{ \
@@ -253,7 +257,7 @@
 	     conv/conv_gsm0503_test.ok endian/endian_test.ok 		\
 	     sercomm/sercomm_test.ok prbs/prbs_test.ok			\
 	     gsm23003/gsm23003_test.ok                                 \
-	     timer/clk_override_test.ok
+	     timer/clk_override_test.ok nego/nego_test.ok
 
 DISTCLEANFILES = atconfig atlocal conv/gsm0503_test_vectors.c
 BUILT_SOURCES = conv/gsm0503_test_vectors.c
diff --git a/tests/nego/nego_test.c b/tests/nego/nego_test.c
new file mode 100644
index 0000000..ec19f16
--- /dev/null
+++ b/tests/nego/nego_test.c
@@ -0,0 +1,170 @@
+/*
+ * (C) 2018 by Vadim Yanitskiy <axilirator at gmail.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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.
+ *
+ */
+
+#include <osmocom/core/negotiation.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#define NEGO_MSGB_SIZE 256
+
+enum nego_test_feature_tag {
+	NEGO_TEST_FEATURE_F0 = 0,
+	NEGO_TEST_FEATURE_F1,
+	NEGO_TEST_FEATURE_F2,
+	NEGO_TEST_FEATURE_F3,
+	NEGO_TEST_FEATURE_F4,
+	NEGO_TEST_FEATURE_F5,
+	_NEGO_TEST_FEATURE_MAX
+};
+
+void nego_test_features(void)
+{
+	struct msgb *msg;
+	bool check;
+
+	printf("[T] Testing negotiation of features...\n");
+
+	msg = msgb_alloc(NEGO_MSGB_SIZE, "nego_features");
+	OSMO_ASSERT(msg);
+
+	printf("[i] Encoding a few features\n");
+	osmo_nego_add_feature(msg, NEGO_TEST_FEATURE_F2);
+	osmo_nego_add_feature(msg, NEGO_TEST_FEATURE_F5);
+	printf("[?] Message: %s\n", msgb_hexdump(msg));
+
+	printf("[i] Verifying recently encoded features...\n");
+	check = osmo_nego_check_feature(msg->data,
+		msgb_length(msg), NEGO_TEST_FEATURE_F2);
+	OSMO_ASSERT(check);
+	check = osmo_nego_check_feature(msg->data,
+		msgb_length(msg), NEGO_TEST_FEATURE_F5);
+	OSMO_ASSERT(check);
+	printf("[+] Expected features preset\n");
+
+	printf("[i] Verifying non-existing features...\n");
+	check = osmo_nego_check_feature(msg->data,
+		msgb_length(msg), NEGO_TEST_FEATURE_F1);
+	OSMO_ASSERT(!check);
+	check = osmo_nego_check_feature(msg->data,
+		msgb_length(msg), NEGO_TEST_FEATURE_F3);
+	OSMO_ASSERT(!check);
+	check = osmo_nego_check_feature(msg->data,
+		msgb_length(msg), NEGO_TEST_FEATURE_F4);
+	OSMO_ASSERT(!check);
+	printf("[+] Unexpected features not preset\n");
+
+	printf("[i] Adding some more features...\n");
+	osmo_nego_add_feature(msg, NEGO_TEST_FEATURE_F1);
+	osmo_nego_add_feature(msg, NEGO_TEST_FEATURE_F3);
+	printf("[?] Message: %s\n", msgb_hexdump(msg));
+
+	msgb_free(msg);
+	printf("\n");
+}
+
+enum nego_test_info_tag {
+	NEGO_TEST_INFO_PROP0 = 0,
+	NEGO_TEST_INFO_PROP1,
+	NEGO_TEST_INFO_PROP2,
+	NEGO_TEST_INFO_PROP3,
+	_NEGO_TEST_INFO_MAX
+};
+
+static const struct value_string info_list[] = {
+	OSMO_VALUE_STRING(NEGO_TEST_INFO_PROP0),
+	OSMO_VALUE_STRING(NEGO_TEST_INFO_PROP1),
+	/* NEGO_TEST_INFO_PROP2 is intentionally not included */
+	OSMO_VALUE_STRING(NEGO_TEST_INFO_PROP3),
+	{ 0, NULL }
+};
+
+int dump_encoded_info(struct msgb *msg, bool print_info)
+{
+	const char *info_str;
+	int count = 0;
+	uint8_t tag;
+
+	for (tag = 0; tag < _NEGO_TEST_INFO_MAX; tag++) {
+		info_str = osmo_nego_get_info(msg->data, msgb_length(msg), tag);
+		if (!info_str)
+			continue;
+
+		if (print_info)
+			printf("[?] Decoded info: tag 0x%02x, value '%s'\n", tag, info_str);
+
+		count++;
+	}
+
+	return count;
+}
+
+void nego_test_info(void)
+{
+	const char *info_str;
+	struct msgb *msg;
+	int rc, len;
+
+	printf("[T] Testing info negotiation...\n");
+
+	msg = msgb_alloc(NEGO_MSGB_SIZE, "nego_info");
+	OSMO_ASSERT(msg);
+
+	printf("[i] Encoding info list...\n");
+	rc = osmo_nego_enc_info(msg, info_list);
+	OSMO_ASSERT(rc > 0);
+	printf("[?] Encoded %d chunks\n", rc);
+	printf("[?] Message: %s\n", msgb_hexdump(msg));
+
+	printf("[i] Checking encoded info...\n");
+	dump_encoded_info(msg, true);
+
+	printf("[i] Trying to get missing info...\n");
+	info_str = osmo_nego_get_info(msg->data,
+		msgb_length(msg), NEGO_TEST_INFO_PROP2);
+	OSMO_ASSERT(info_str == NULL);
+	printf("[+] Unexpected info not preset\n");
+
+	printf("[i] Checking buffer limiting...\n");
+	len = msgb_length(msg);
+	do {
+		msgb_trim(msg, len);
+		rc = dump_encoded_info(msg, false);
+		printf("[?] Buffer len=%d, decoded=%d\n", len, rc);
+	} while (--len >= 0);
+
+	msgb_free(msg);
+	printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+	void *ctx = talloc_init("nego_test");
+	msgb_talloc_ctx_init(ctx, 0);
+
+	nego_test_features();
+	nego_test_info();
+
+	printf("[i] Success!\n");
+	return 0;
+}
diff --git a/tests/nego/nego_test.ok b/tests/nego/nego_test.ok
new file mode 100644
index 0000000..285b089
--- /dev/null
+++ b/tests/nego/nego_test.ok
@@ -0,0 +1,93 @@
+[T] Testing negotiation of features...
+[i] Encoding a few features
+[?] Message: 02 05 
+[i] Verifying recently encoded features...
+[+] Expected features preset
+[i] Verifying non-existing features...
+[+] Unexpected features not preset
+[i] Adding some more features...
+[?] Message: 02 05 01 03 
+
+[T] Testing info negotiation...
+[i] Encoding info list...
+[?] Encoded 3 chunks
+[?] Message: 00 15 4e 45 47 4f 5f 54 45 53 54 5f 49 4e 46 4f 5f 50 52 4f 50 30 00 01 15 4e 45 47 4f 5f 54 45 53 54 5f 49 4e 46 4f 5f 50 52 4f 50 31 00 03 15 4e 45 47 4f 5f 54 45 53 54 5f 49 4e 46 4f 5f 50 52 4f 50 33 00 
+[i] Checking encoded info...
+[?] Decoded info: tag 0x00, value 'NEGO_TEST_INFO_PROP0'
+[?] Decoded info: tag 0x01, value 'NEGO_TEST_INFO_PROP1'
+[?] Decoded info: tag 0x03, value 'NEGO_TEST_INFO_PROP3'
+[i] Trying to get missing info...
+[+] Unexpected info not preset
+[i] Checking buffer limiting...
+[?] Buffer len=69, decoded=3
+[?] Buffer len=68, decoded=2
+[?] Buffer len=67, decoded=2
+[?] Buffer len=66, decoded=2
+[?] Buffer len=65, decoded=2
+[?] Buffer len=64, decoded=2
+[?] Buffer len=63, decoded=2
+[?] Buffer len=62, decoded=2
+[?] Buffer len=61, decoded=2
+[?] Buffer len=60, decoded=2
+[?] Buffer len=59, decoded=2
+[?] Buffer len=58, decoded=2
+[?] Buffer len=57, decoded=2
+[?] Buffer len=56, decoded=2
+[?] Buffer len=55, decoded=2
+[?] Buffer len=54, decoded=2
+[?] Buffer len=53, decoded=2
+[?] Buffer len=52, decoded=2
+[?] Buffer len=51, decoded=2
+[?] Buffer len=50, decoded=2
+[?] Buffer len=49, decoded=2
+[?] Buffer len=48, decoded=2
+[?] Buffer len=47, decoded=2
+[?] Buffer len=46, decoded=2
+[?] Buffer len=45, decoded=1
+[?] Buffer len=44, decoded=1
+[?] Buffer len=43, decoded=1
+[?] Buffer len=42, decoded=1
+[?] Buffer len=41, decoded=1
+[?] Buffer len=40, decoded=1
+[?] Buffer len=39, decoded=1
+[?] Buffer len=38, decoded=1
+[?] Buffer len=37, decoded=1
+[?] Buffer len=36, decoded=1
+[?] Buffer len=35, decoded=1
+[?] Buffer len=34, decoded=1
+[?] Buffer len=33, decoded=1
+[?] Buffer len=32, decoded=1
+[?] Buffer len=31, decoded=1
+[?] Buffer len=30, decoded=1
+[?] Buffer len=29, decoded=1
+[?] Buffer len=28, decoded=1
+[?] Buffer len=27, decoded=1
+[?] Buffer len=26, decoded=1
+[?] Buffer len=25, decoded=1
+[?] Buffer len=24, decoded=1
+[?] Buffer len=23, decoded=1
+[?] Buffer len=22, decoded=0
+[?] Buffer len=21, decoded=0
+[?] Buffer len=20, decoded=0
+[?] Buffer len=19, decoded=0
+[?] Buffer len=18, decoded=0
+[?] Buffer len=17, decoded=0
+[?] Buffer len=16, decoded=0
+[?] Buffer len=15, decoded=0
+[?] Buffer len=14, decoded=0
+[?] Buffer len=13, decoded=0
+[?] Buffer len=12, decoded=0
+[?] Buffer len=11, decoded=0
+[?] Buffer len=10, decoded=0
+[?] Buffer len=9, decoded=0
+[?] Buffer len=8, decoded=0
+[?] Buffer len=7, decoded=0
+[?] Buffer len=6, decoded=0
+[?] Buffer len=5, decoded=0
+[?] Buffer len=4, decoded=0
+[?] Buffer len=3, decoded=0
+[?] Buffer len=2, decoded=0
+[?] Buffer len=1, decoded=0
+[?] Buffer len=0, decoded=0
+
+[i] Success!
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 15a89b6..5ab3373 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -311,3 +311,9 @@
 cat $abs_srcdir/gsm23003/gsm23003_test.ok > expout
 AT_CHECK([$abs_top_builddir/tests/gsm23003/gsm23003_test], [0], [expout], [ignore])
 AT_CLEANUP
+
+AT_SETUP([nego])
+AT_KEYWORDS([nego])
+cat $abs_srcdir/nego/nego_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/nego/nego_test], [0], [expout], [ignore])
+AT_CLEANUP

-- 
To view, visit https://gerrit.osmocom.org/10034
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I6fd5b6be6755d4aa735a510e95459180225389ba
Gerrit-Change-Number: 10034
Gerrit-PatchSet: 1
Gerrit-Owner: Vadim Yanitskiy <axilirator at gmail.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20180718/9694b85f/attachment.htm>


More information about the gerrit-log mailing list