From patchwork Wed Dec 13 21:51:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Denis Kenzior X-Patchwork-Id: 13491915 Received: from mail-ot1-f41.google.com (mail-ot1-f41.google.com [209.85.210.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5F015C084 for ; Wed, 13 Dec 2023 21:52:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="A6XYH7U/" Received: by mail-ot1-f41.google.com with SMTP id 46e09a7af769-6d9e756cf32so4930426a34.2 for ; Wed, 13 Dec 2023 13:52:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1702504341; x=1703109141; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=duRuQpcYqQGqy8vucHn13pHPeIPskYlgbS6cuGOPPUE=; b=A6XYH7U/kiqysqjFXrgHT+lfQUhgsS9m++8g+LRWNdF3zX5Gl/jPARJ0itvQ1d9+lo fR12Gi563bHbQLbMlwnxH96sPhl22NqFabWVZxsQ7SQ0UGe8xozK5ojhshFSO+j9n5lG yS2Ccx6LHy0AS5v93flZOMBIur3op4j5Mi2u15loSPjAa1ySk54tSNxsX1Vi2XqU8oZs asXvWGApRckP7jhPmyK+R8Pmx4R+fI0JnhCRg2jo1Axv2HcDh/r+YCTbtMIu50gOenb3 O/sKDtC2VD+/A+/ImEZTn2gRtCT2OA21BKeTCDQpoH1gsnT6JLJ8hL6GfrmzbwFdd46T /16A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702504341; x=1703109141; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=duRuQpcYqQGqy8vucHn13pHPeIPskYlgbS6cuGOPPUE=; b=YHGFKaqaGQjGzNHiT9gtBf0SUsVWdUgtuuUb+yhwvoHiDLHB2GSvu+7EWfh0Je02TC Yext76kcZdo58qbANRD68ol83jMblUmiBpbMzGf7tu9V2Y8BN9gHLe0gfJkgj7X6dixD 59+b2/h9o8W+f82h3lBMIaBUNlYHvEZ+gUgyL3W2UXzVs4rBryg7Kg8MQxs+qMoFfVdD RdKRHbmPo/awKw2HTt+QAG2q9QIYWDHJOXYNFyvq+AaWP9wO2h8Z+uL1qmtHwyccO9kP q7p+RNGV2t8aS9UhSeNrYML33rwhPDiJpIstlJlnA65W/JM6aEzbtLGUVvUAaQ91+meg I6gw== X-Gm-Message-State: AOJu0YwB1QupHbrj037Qxd7P2IYSTLnOHLnf9ZZ1dc/Pr+YrCw/6CnKA i9bXa8LYO6u2b/UIsRPGIHINjmg8biM= X-Google-Smtp-Source: AGHT+IFihXdPE6zFPWozPlqlE07/5aeEqqhQUU6sHyN1a+0teflkKJS96PqtBxfXa8sKdQhDz5a2qw== X-Received: by 2002:a05:6830:1e73:b0:6da:32d9:4166 with SMTP id m19-20020a0568301e7300b006da32d94166mr2228505otr.18.1702504341481; Wed, 13 Dec 2023 13:52:21 -0800 (PST) Received: from localhost.localdomain (070-114-247-242.res.spectrum.com. [70.114.247.242]) by smtp.gmail.com with ESMTPSA id n18-20020a9d7412000000b006ce33ba6474sm2925612otk.4.2023.12.13.13.52.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Dec 2023 13:52:21 -0800 (PST) From: Denis Kenzior To: ofono@lists.linux.dev Cc: Denis Kenzior Subject: [PATCH 1/6] build: Fix typo that breaks --fsanitize=leak check Date: Wed, 13 Dec 2023 15:51:25 -0600 Message-ID: <20231213215132.287577-1-denkenz@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: ofono@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- acinclude.m4 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 4932ac6b..656888d7 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -23,8 +23,7 @@ AC_DEFUN([AC_PROG_CC_ASAN], [ ]) AC_DEFUN([AC_PROG_CC_LSAN], [ - AC_CACHE_CHECK([whether ${CC-cc} accepts -fsanitize=leak], ac_cv_prog_cc -_lsan, [ + AC_CACHE_CHECK([whether ${CC-cc} accepts -fsanitize=leak], ac_cv_prog_cc_lsan, [ echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -fsanitize=leak -c conftest.c 2>&1`"; then ac_cv_prog_cc_lsan=yes From patchwork Wed Dec 13 21:51:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Denis Kenzior X-Patchwork-Id: 13491916 Received: from mail-oi1-f174.google.com (mail-oi1-f174.google.com [209.85.167.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 579447FBA9 for ; Wed, 13 Dec 2023 21:52:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="A5d7VfEy" Received: by mail-oi1-f174.google.com with SMTP id 5614622812f47-3b9de2332e3so5097120b6e.1 for ; Wed, 13 Dec 2023 13:52:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1702504342; x=1703109142; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=GckDHAAXjIDXvCmCp7SE0lE8M0eR/e2fqstu5GHWGJQ=; b=A5d7VfEy3RX1pROmEDheBkFBTH9hb4jS1fDBEdGSczwCRkW0I2KndCVQNeLXr+vTI8 x3CK6C+kLDDgadEqUH2Bdq3NXA8Sz2XajuFiGlLrNsZKDlTSrM1TpbzAu2D+4mD/AUAc 9G3YAbxFf/ZqgrxwIUObEmilKEE4USqqQg60u9dsNUct4dBCk1zcPPakrhOQqlugaHyC a2eupQaDv13ttHdysveuUcrWkA/LD7tGFbxtaGm1+2Z8Sae8uHHfyauBbZicHCNx5tL1 OpT1J53TGexQk/HfgLdmJj5F7nkQMrHENwtKxGUaxtrXGqt5VKVdVBH93n9sQyKXpKQl P0qw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702504342; x=1703109142; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=GckDHAAXjIDXvCmCp7SE0lE8M0eR/e2fqstu5GHWGJQ=; b=K5gjfTiZ+JWvUfGDKys9j8xeNRkdZkiMRJ3BZASgomYVyOmCmWVSK5K/i/HZnIq0Tf C6m7YcAPAhSADg4vezn8PBv8/8+vH5Wl1Glo7UakGtK2DqhlVC++Fw5B++vYx9+cC1Jf Ipo4PSlc6UelPwXr3RfVlaDnaFvtmyWFp3QVLWHaThC06+StcvXFoz7gyLaUuLvsUslj 5Hm2WJJaQBVk6pPFVuISEs37SCFdvd49s//+WM4hXQxe/fKeFDGAiWtGH4GZvDGiPBnj N0cpgk3z6mFHGH+VR7t1BHfUVSZFgGiD2gHsLvnCj8AsWBxlWjS0777OIxToLQVKdMTw bX6w== X-Gm-Message-State: AOJu0YxLhNGN7ukZy1x3qKUagclyZ/fl1805ppXyNEyPWBLigcfV6ENB bv9urI328msPbhWDvPcZM52AMHu/Q38= X-Google-Smtp-Source: AGHT+IH3ObPb118DidIEBLBfAFuyQLX0rEzPIDgmfxtLDIehvKfDe/ICxX2V/Wh6gzAL/pZ5V0E0bQ== X-Received: by 2002:a05:6830:20cd:b0:6d9:dbe2:5ec2 with SMTP id z13-20020a05683020cd00b006d9dbe25ec2mr8324330otq.69.1702504342115; Wed, 13 Dec 2023 13:52:22 -0800 (PST) Received: from localhost.localdomain (070-114-247-242.res.spectrum.com. [70.114.247.242]) by smtp.gmail.com with ESMTPSA id n18-20020a9d7412000000b006ce33ba6474sm2925612otk.4.2023.12.13.13.52.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Dec 2023 13:52:21 -0800 (PST) From: Denis Kenzior To: ofono@lists.linux.dev Cc: Denis Kenzior Subject: [PATCH 2/6] include: Allow multiple context types Date: Wed, 13 Dec 2023 15:51:26 -0600 Message-ID: <20231213215132.287577-2-denkenz@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231213215132.287577-1-denkenz@gmail.com> References: <20231213215132.287577-1-denkenz@gmail.com> Precedence: bulk X-Mailing-List: ofono@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- include/gprs-context.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/gprs-context.h b/include/gprs-context.h index 75958284..81055d89 100644 --- a/include/gprs-context.h +++ b/include/gprs-context.h @@ -33,10 +33,10 @@ struct ofono_modem; enum ofono_gprs_context_type { OFONO_GPRS_CONTEXT_TYPE_ANY = 0, - OFONO_GPRS_CONTEXT_TYPE_INTERNET, - OFONO_GPRS_CONTEXT_TYPE_MMS, - OFONO_GPRS_CONTEXT_TYPE_WAP, - OFONO_GPRS_CONTEXT_TYPE_IMS, + OFONO_GPRS_CONTEXT_TYPE_INTERNET = 0x0001, + OFONO_GPRS_CONTEXT_TYPE_MMS = 0x0002, + OFONO_GPRS_CONTEXT_TYPE_WAP = 0x0004, + OFONO_GPRS_CONTEXT_TYPE_IMS = 0x0008, }; struct ofono_gprs_primary_context { From patchwork Wed Dec 13 21:51:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Denis Kenzior X-Patchwork-Id: 13491919 Received: from mail-ot1-f49.google.com (mail-ot1-f49.google.com [209.85.210.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C61AF7FBAA for ; Wed, 13 Dec 2023 21:52:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gOx8CVuL" Received: by mail-ot1-f49.google.com with SMTP id 46e09a7af769-6d9e0f0cba9so4809556a34.1 for ; Wed, 13 Dec 2023 13:52:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1702504342; x=1703109142; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Z1KVEnnKDmjqDOTqvPpuKzmwPHwR+atNx4ptG5ByOt8=; b=gOx8CVuLjmwrn1HggnibTRRl4in495htibVXumsH1MYp/2foYwmFoAUAmVtUSMUCBG 8aL6nXhEo9YW5YL+8NhvD2I7CwLVyaMLgjiT+s6UyYJhRAcEOjBm48BEIpDjh62iDXSg iXbcdjgWoeyV0pEZf43MR3/kbrWPfLxHRub6HmI+k9XMO6C/LTSu1/ziS99B9Vo2YEEA xK9R8HIU0AM5ssShFW+ZnqrvqRxveD7a0DBl0ybCay0zjRDqZf8/7acKpLoW75qh7MMn 3JPDKeRFf9MCq7psUzPTz9/QDhxQklx3WDZ4F+oG0bvPejduFjl4NzK+BaybHZO2HCkV WBUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702504342; x=1703109142; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Z1KVEnnKDmjqDOTqvPpuKzmwPHwR+atNx4ptG5ByOt8=; b=q2f6xrZqdoWj8cxGvVq9ozVZ2cOXZDURhxH1wz2FD8/cur8WaeAFZFozRN+oqYP1j/ cPBqeCh6j2ySRgLZioz3sYy6D2G15gFYwIs40S4Dnz5z64GqxbXevrLe3MbAbIuC7Afs tG6IS5KLFBQBDB7tf58OiT+9R/VeRd1JcUxzZ0OZOY89p9Ap1cOEfeOFEta2Rubw7AU7 P1tAOLSzcJHA8X18RAcvbN+D/Z0KN+2InHG94PVyC3nhNqi7LLgYBIOIxxLDm4h3KeMV oN9dI/P3YfkInJJ/UhJUBA5Ws/XbNtClCQ//Otz6Heka2fnsNV1WU+Gl0C9H6wIm1Aee U7WQ== X-Gm-Message-State: AOJu0YxmdCa2zuFq4jOvsJjbQZkOM2F354foozuyc0Z9Lm11+6gCeWbN QhHIcaIljVup3Qob+YhJYQlQOH+xNho= X-Google-Smtp-Source: AGHT+IGW2c12N6YzTu1KgjiPbx0OBWkWAuYotX/ymI0t8UJN8k+tv7ZxHadNNuYyWextTtI9gDTBPw== X-Received: by 2002:a9d:77ca:0:b0:6d9:d368:65f6 with SMTP id w10-20020a9d77ca000000b006d9d36865f6mr7880693otl.22.1702504342726; Wed, 13 Dec 2023 13:52:22 -0800 (PST) Received: from localhost.localdomain (070-114-247-242.res.spectrum.com. [70.114.247.242]) by smtp.gmail.com with ESMTPSA id n18-20020a9d7412000000b006ce33ba6474sm2925612otk.4.2023.12.13.13.52.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Dec 2023 13:52:22 -0800 (PST) From: Denis Kenzior To: ofono@lists.linux.dev Cc: Denis Kenzior Subject: [PATCH 3/6] doc: docs for intermediate provisioning db format Date: Wed, 13 Dec 2023 15:51:27 -0600 Message-ID: <20231213215132.287577-3-denkenz@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231213215132.287577-1-denkenz@gmail.com> References: <20231213215132.287577-1-denkenz@gmail.com> Precedence: bulk X-Mailing-List: ofono@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Introduce a new provisioning file format to be used by oFono. This format will be an 'intermediate' format, that will be converted to a binary format for use on the actual device. This allows expensive and error prone (not to mention hard to secure) parsing of XML files (such asmobile-broadband-provider-info or Android apns-conf.xml) to be avoided. JSON was chosen since it is a much more readable format compared to XML. --- doc/provision.rst | 139 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 doc/provision.rst diff --git a/doc/provision.rst b/doc/provision.rst new file mode 100644 index 00000000..693b38a3 --- /dev/null +++ b/doc/provision.rst @@ -0,0 +1,139 @@ +Carrier Settings Provisioning Database +====================================== + +Carrier settings provisioning is performed automatically whenever a new, +not previously used SIM card is detected for the first time. Settings such as +APN, username, password settings as well as the default bearer settings can be +provisioned automatically if a matching database entry is found. + +Matches are performed based on the following information: + * SIM Mobile Country Code (MCC) + * SIM Mobile Network Code (MNC) + * SIM Service Provider Name (SPN) + +The carrier settings provisioning database is represented in JSON format and is +converted to a binary format at installation time. + +JSON Structure +-------------- +**(List):** + List of mobile network provider objects. Top level element. + + Example: + ``[{"name": "Operator XYZ", ...}, {"name": "Operator ZYX", ...}]`` + + - **name (String):** + The name of the mobile network provider. This field is a freeform + string that identifies the provider. This field is used purely for + human consumption and not used by the provisioning logic. + + This field is `required`. + + Example: "Operator XYZ" + + - **ids (List of Strings):** + Unique identifiers associated with the mobile network provider. This + is a list of all MCC+MNC identifiers associated with this provider. + + This field is `required`. + + Example: ``["99955", "99956", "99901", "99902"]`` + + - **spn (String):** + Service Provider Name associated with the mobile network provider. This + field is typically used to differentiate MVNOs from non-MVNO providers. + + This field is `optional`. + + Example: "ZYX" + + - **apns (List):** + List of access points associated with the mobile network provider. At + least one entry must be present in the list. + + This field is `required`. + + - **name (String):** + The descriptive name of the access point. If present, will be + reflected in the oFono context name after successful provisioning. + + This field is `optional`. + + Example: "Internet" + + - **apn (String):** + Access Point Name - Setting required for successful bearer + activation. Provided by the carrier. + + This field is `required`. + + Example: "internet" + + - **type (List of Strings):** + The types of connections supported by the access point. The + following types are recognized: + + - "internet": Used for general internet access. + - "mms": Used for Multimedia Messaging Service (MMS). + - "wap": Used for Wireless Application Protocol (WAP). + - "ims": Used for IP Multimedia Subsystem (IMS). + - "supl": Used for Secure User Plane Location (SUPL). + - "ia": Used for Initial Attach in LTE networks. + + This field is `required`. + + Example: ``["internet", "mms"]`` + + - **authentication (String):** + Authentication method used for the connection. The following types + are recognized: + + - "chap": CHAP authentication + - "pap": PAP authentication + - "none": No authentication is used + + This field is `optional`. + + Example: "none" + + - **username (String):** + Username used for autenticating to the access point. + + This field is `optional`. + + Example: "username" + + - **password (String):** + Password used for autenticating to the access point. + + This field is `optional`. + + Example: "temp123" + + - **protocol (String):** + Network protocol used for the connection. The following types are + recognized: + + - "ipv4": IPv4 only + - "ipv6": IPv6 only + - "ipv4v6": Dual protocol, both IPv4 and IPv6 will be negotiated + + If omitted, then `"ipv4v6"` will be assumed. + + This field is `optional`. + + Example: "ipv4" + + - **mmsc (String):** + Multimedia Messaging Service Center - URL for MMS. + + This field is `required` for MMS contexts. + + Example: "foobar.mmsc:80" + + - **mmsproxy (String):** + Proxy server for Multimedia Messaging Service (MMS). + + This field is `optional`. + + Example: "mms.proxy.net" From patchwork Wed Dec 13 21:51:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Denis Kenzior X-Patchwork-Id: 13491920 Received: from mail-ot1-f50.google.com (mail-ot1-f50.google.com [209.85.210.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E53015C084 for ; Wed, 13 Dec 2023 21:52:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QUaYyFVO" Received: by mail-ot1-f50.google.com with SMTP id 46e09a7af769-6d9dadc3dc0so5743266a34.1 for ; Wed, 13 Dec 2023 13:52:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1702504344; x=1703109144; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=YQDggSCoMGq/3sphnktLeOhMCJdkvb4KhfZrw/FYdPY=; b=QUaYyFVO3GieNgC4JNiKGhA9mdyiB1BQJtLK4kILAEN9Yc7toaMsxUdfGhOCWMBzeb IeFPLbh/bvitQ0uuEioyWwFPniVv788Ohlntq5vMSJRZzQImXdM1vgQ3mtqaIjIZQl+2 wlOkiDgkdqm7N6/dEseGu58BFONY7HoegDk+HEK00flObRN7KJd2mlM5FuAoUDUGg9I/ f7i0jqAHgBvsYeBNzIxaNsssXCK4ng4GGjphstzGEC7d8RWAiGjS0QHGBVFPXRfPNIXu dwmPR9CgW9XdlpNi8fBsjiUTF5UjeI1iTplXZA6cLAgs7Vc/mGz3K4Ut/AEJQ+aVonxH 6wOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702504344; x=1703109144; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=YQDggSCoMGq/3sphnktLeOhMCJdkvb4KhfZrw/FYdPY=; b=hG7CnEmEw+QLJeldOO6lJzQDAP+sb3MUnvpUy23uGj87x6+zdjzqxN4EFw4QDF5TNt botoKAhJ7g7tw94jozRUfamqafB953EKoyQL9Bd/mr6On8VwE8m9aAkDElKkQ5X7dAy7 tw9A17LeYYBsiUDDJ9+5khHNL5Ci396eCcB4gul/gsgOPUTUNWZ92CDzuS9S9WS7w8HB m3CYZmw2/RMnbXOz8Eb9Vg6WaT8Tjt55Pbtbay5YidigMrEJ1K6WP5iKOFgP5ZZMyFea 0/KYB5z3IbE3HlGuDWJmD/riCvXoo/iWOcw1P1a+k7iTdr9HfUJBvQRDfCCPkKAnm/GJ 7fFA== X-Gm-Message-State: AOJu0Yw0loLlYZjOwb9pZx1C8Gx8D7hZ8exNMleS4e4GHA8N2dh/8kwm mokNcxu6qXBFUodveKgco+s2c9aHubo= X-Google-Smtp-Source: AGHT+IH7jvheXxyRgA/eouJ3RztH3KUeTxaYmacUfbDJ3sZlEjUgBivA3bXTcmuUnxrq79t88wGkPQ== X-Received: by 2002:a9d:7dd8:0:b0:6d9:d2ef:f923 with SMTP id k24-20020a9d7dd8000000b006d9d2eff923mr8444337otn.74.1702504343659; Wed, 13 Dec 2023 13:52:23 -0800 (PST) Received: from localhost.localdomain (070-114-247-242.res.spectrum.com. [70.114.247.242]) by smtp.gmail.com with ESMTPSA id n18-20020a9d7412000000b006ce33ba6474sm2925612otk.4.2023.12.13.13.52.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Dec 2023 13:52:23 -0800 (PST) From: Denis Kenzior To: ofono@lists.linux.dev Cc: Denis Kenzior Subject: [PATCH 4/6] tools: Add provision.py Date: Wed, 13 Dec 2023 15:51:28 -0600 Message-ID: <20231213215132.287577-4-denkenz@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231213215132.287577-1-denkenz@gmail.com> References: <20231213215132.287577-1-denkenz@gmail.com> Precedence: bulk X-Mailing-List: ofono@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Introduce a new tool that will convert the intermediate JSON format (documented in doc/provision.rst) to the binary provisioning format, which will be used by ofonod's default provisioning plugin for automatically setting up carrier specific settings. The tool also supports import of and conversion of 'mobile-broadband-provider-info' XML files to the new intermediate JSON file format. This is accomplished using: % tools/provision.py mbpi-convert --outfile=provision.json Conversion of JSON intermediate format to binary format is accomplished using: % tools/provision.py generate --infile=provision.json By default, the output will be placed in the same directory in the file 'provision.db'. Alternatively, the output file can be specified using the --outfile option. Finally, the tool supports a simple selftest method, which can be invoked as follows: % tools/provision.py selftest --- tools/provision.py | 727 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 727 insertions(+) create mode 100755 tools/provision.py diff --git a/tools/provision.py b/tools/provision.py new file mode 100755 index 00000000..79273277 --- /dev/null +++ b/tools/provision.py @@ -0,0 +1,727 @@ +#!/usr/bin/python3 +# +# oFono - Open Source Telephony +# Copyright (C) 2023 Cruise, LLC +# +# SPDX-License-Identifier: GPL-2.0-only +import xml.etree.ElementTree as ET +import sys +import json +import bisect +from argparse import ArgumentParser, FileType +from pathlib import Path +import random +import struct +import ctypes + +class ProviderInfo: + sort_order_map = { v : pos for pos, v in + enumerate( ['name', + 'apn', + 'type', + 'protocol', + 'mmsc', + 'mmsproxy', + 'authentication', + 'username', + 'password'] ) } + + @classmethod + def rawimport(cls, entry): + if 'name' not in entry: + raise SystemExit('No name for entry: ' + str(entry)) + + info = ProviderInfo(entry['name']) + + for networkid in entry.get('ids', []): + if not info.add_id(networkid): + raise SystemExit('Invalid network id: ' + str(networkid)) + + if 'spn' in entry: + if not info.set_spn(entry['spn']): + raise SystemExit('Invalid spn: ' + str(spn)) + + for apn in entry.get('apns', []): + if not info.add_context(apn): + raise SystemExit('Invalid apn: ' + str(apn)) + + if not info.is_valid(): + raise SystemExit('Invalid entry: ' + str(entry)) + + return info + + def __init__(self, name): + self.context_list = [] + self.mccmnc_list = [] + self.name = name + self.spn = None + + @staticmethod + def is_valid_id(id_string, expected_lengths): + """ + Check if the identifier string is valid. + + Parameters: + - id_string: The id string to check. + - expected_lengths (tuple): A tuple representing the valid range of + lengths. + + Returns: + - bool: True if the MCC string is valid, False otherwise. + """ + if not id_string.isdigit(): + return False + + if len(id_string) not in expected_lengths: + return False + + if int(id_string) == 0: + return False + + return True + + def add_mccmnc(self, mcc, mnc): + if not self.is_valid_id(mcc, (3,)) or not self.is_valid_id(mnc, (2, 3)): + return False + + bisect.insort(self.mccmnc_list, mcc + mnc) + return True + + def add_id(self, mccmnc): + if not self.is_valid_id(mccmnc, (5,6)): + return False + + if int(mccmnc[:3]) == 0 or int(mccmnc[3:]) == 0: + return False + + bisect.insort(self.mccmnc_list, mccmnc) + return True + + def set_spn(self, spn): + if len(spn) == 0 or len(spn) > 254: + return False + + self.spn = spn + return True + + def add_context(self, info): + info = dict(sorted(info.items(), + key = lambda pair: self.sort_order_map[pair[0]])) + self.context_list.append(info) + + return True + + def is_valid(self): + return len(self.context_list) and len(self.mccmnc_list) + + def __str__(self): + s = 'Provider \'' + self.name + '\'' + + if (self.spn != None): + s += ' [SPN:\'' + self.spn + '\']' + + s+= ' ' + str(self.mccmnc_list) + '\n' + + for context in self.context_list: + s += '\t' + str(context) + '\n' + + return s + +class MobileBroadbandProviderInfo: + usage_to_type = { 'internet' : ['internet'], + 'mms' : ['mms'], + 'wap' : ['wap'], + 'mms-internet-hipri' : ['internet', 'mms'], + 'mms-internet-hipri-fota' : ['internet','mms'], + } + @classmethod + def type_from_usage(cls, usage): + return cls.usage_to_type[usage] + + def __init__(self, xml_path): + self.tree = ET.parse(xml_path) + + def parse(self, xml_path): + providers = [] + + try: + tree = ET.parse(xml_path) + root = tree.getroot() + + for provider in root.findall('.//provider'): + name = provider.find('name') + if name is None or not name.text: + continue; + + info = ProviderInfo(name.text) + + for networkid in provider.findall('gsm/network-id'): + info.add_mccmnc(networkid.get('mcc'), networkid.get('mnc')) + + for apn in provider.findall('gsm/apn'): + context = {} + + context['apn'] = apn.get('value') + if context['apn'] == None: + continue + + # Usage is missing for some APNs, skip such contexts for now + usage = apn.find('usage') + if usage is None or usage.get('type') is None: + continue; + + context['type'] = self.type_from_usage(usage.get('type')) + if context['type'] == None: + sys.stderr.write("Unable to convert type: %s\n" % + usage.get('type')) + continue + + if 'mms' in context['type']: + mmsc = apn.find('mmsc') + + # Ignore MMS contexts with no MMSC since it is needed + # to send messages + if mmsc is None or not mmsc.text: + continue + + context['mmsc'] = mmsc.text + + mmsproxy = apn.find('mmsproxy') + if mmsproxy is not None and mmsproxy.text: + context['mmsproxy'] = mmsproxy.text + + username = apn.find('username') + if username is not None and username.text: + context['username'] = username.text + + password = apn.find('password') + if password is not None and password.text: + context['password'] = password.text + + authentication = apn.find('authentication') + if authentication is not None: + context['authentication'] = authentication.get('method') + + context_name = apn.find('name') + if context_name != None: + context['name'] = context_name.text + + info.add_context(context) + + if info.is_valid(): + providers.append(info) + + except ET.ParseError as e: + print(f"Error parsing XML: {e}") + + return providers + +class ProvisionContext(ctypes.LittleEndianStructure): + _pack_ = 1 + _fields_ = [ + ('type', ctypes.c_uint32), + ('protocol', ctypes.c_uint32), + ('authentication', ctypes.c_uint32), + ('reserved', ctypes.c_uint32), + ('name_offset', ctypes.c_uint64), + ('apn_offset', ctypes.c_uint64), + ('username_offset', ctypes.c_uint64), + ('password_offset', ctypes.c_uint64), + ('mmsproxy_offset', ctypes.c_uint64), + ('mmsc_offset', ctypes.c_uint64) + ] + + authentication_dict = { 'chap' : 0, 'pap' : 1, 'none' : 2 } + protocol_dict = { 'ipv4' : 0, 'ipv6' : 1, 'ipv4v6' : 2 } + attrs = ['name', 'apn', 'username', 'password', 'mmsproxy', 'mmsc'] + + @classmethod + def type_to_context_type(cls, types): + r = 0 + + for t in types: + if t == 'internet': + r |= 0x0001 + elif t == 'mms': + r |= 0x0002 + elif t == 'wap': + r |= 0x0004 + elif t == 'ims': + r |= 0x0008 + elif t == 'supl': + r |= 0x0010 + elif t == 'ia': + r |= 0x0020 + + return r + + def __init__(self, apn, strings): + self.type = self.type_to_context_type(apn['type']) + self.protocol = self.protocol_dict[apn.get('protocol', 'ipv4v6')] + self.authentication = self.authentication_dict[apn.get('authentication', + 'chap')] + + for s in self.attrs: + offset = strings.add_string(apn.get(s, None)) + setattr(self, s + '_offset', offset) + +class ProvisionData(ctypes.LittleEndianStructure): + _pack_ = 1 + _fields_ = [ + ('spn_offset', ctypes.c_uint64), + ('context_offset', ctypes.c_uint64) + ] + + def __init__(self, spn, offset, strings): + self.spn_offset = strings.add_string(spn) + self.context_offset = offset + +class ProvisionNode(ctypes.LittleEndianStructure): + _pack_ = 1 + _fields_ = [ + ('bit_offsets', ctypes.c_uint64 * 2), + ('mccmnc', ctypes.c_uint32), + ('diff', ctypes.c_int32), + ('provision_data_count', ctypes.c_uint64) + ] + + style = "bold" + fmt_connection = '\t"%s/%d" -> "%s/%d"[color="#%06x"];\n' + fmt_declaration = '\t"%s/%d"[style=%s, color="#%06x"];\n' + red = 0xff0000 + green = 0x00ff00 + + def __init__(self, key, diff): + self.bit = [None, None] + self.key = key + self.diff = diff + self.entries = {} + self.node_offset = 0 + + def choose(self, key): + return (key >> (31 - self.diff)) & 1 + + def print_graphviz(self, f): + f.write(self.fmt_declaration % (format(self.key, '032b'), + self.diff, self.style, + random.randint(0, 0x00ffffff))) + f.write(self.fmt_connection % (format(self.key, '032b'), self.diff, + format(self.bit[0].key, '032b'), + self.bit[0].diff, self.red)) + f.write(self.fmt_connection % (format(self.key, '032b'), self.diff, + format(self.bit[1].key, '032b'), + self.bit[1].diff, self.green)) + + if (self.diff < self.bit[0].diff): + self.bit[0].print_graphviz(f) + + if (self.diff < self.bit[1].diff): + self.bit[1].print_graphviz(f) + + def __str__(self): + s = format(self.key, '032b') + '/' + str(self.diff) + return s + +class MccMncTree: + @staticmethod + def clz(v): + count = 32 + while count and v: + v = v >> 1 + count = count - 1 + + return count + + @staticmethod + def diff(key1, key2): + xor = key1 ^ key2; + return MccMncTree.clz(xor) + + def __init__(self): + self.root = ProvisionNode(key = 0, diff = -1) + self.root.bit[0] = self.root + self.root.bit[1] = self.root + self.n_nodes = 1 + + def print_graphviz(self): + f = open("step%d.dot" % self.n_nodes, "w") + # Use 'dot -Tx11' to visualize + f.write('digraph trie {\n') + self.root.print_graphviz(f) + f.write('}\n') + f.close() + + def find_closest(self, key): + parent = self.root + child = self.root.bit[0] + + while parent.diff < child.diff: + parent = child + child = child.bit[child.choose(key)] + + return child + + def find(self, key): + found = self.find_closest(key) + if found.key == key: + return found + + return None + + def insert(self, key, attr, value): + node = self.find_closest(key); + if node.key == key: + node.entries[attr] = value + return + + bit = self.diff(node.key, key) + parent = self.root + child = self.root.bit[0] + + while (parent.diff < child.diff) and (child.diff < bit): + parent = child + child = child.bit[child.choose(key)] + + node = ProvisionNode(key, bit) + bit = node.choose(key) + node.bit[bit] = node + node.bit[not bit] = child + + node.entries[attr] = value + + if parent == self.root: + self.root.bit[0] = node + else: + bit = parent.choose(key) + parent.bit[bit] = node + + self.n_nodes += 1 + + def traverse_recursive(self, node, bit, visitor): + if node == self.root: + return + + if node.diff <= bit: + visitor.visit(node) + return + + self.traverse_recursive(node.bit[0], node.diff, visitor) + self.traverse_recursive(node.bit[1], node.diff, visitor) + + def traverse(self, visitor): + self.traverse_recursive(self.root.bit[0], -1, visitor) + +class StringAccumulator: + def __init__(self): + self.data = bytearray(b'\x00') # So offsets are never 0 used for NULL + self.offsets = {} + + def add_string(self, s): + if s is None: + return 0 + + if s in self.offsets: + return self.offsets[s] + + offset = len(self.data) + self.data.extend(s.encode('utf-8')) + self.data.append(0) + self.offsets[s] = offset + + return offset + + def get_bytes(self): + return self.data + +class ProvisionDatabase(ctypes.LittleEndianStructure): + _pack_ = 1 + _fields_ = [ + ('version', ctypes.c_uint64), + ('file_size', ctypes.c_uint64), + ('header_size', ctypes.c_uint64), + ('node_struct_size', ctypes.c_uint64), + ('provision_data_struct_size', ctypes.c_uint64), + ('context_struct_size', ctypes.c_uint64), + ('nodes_offset', ctypes.c_uint64), + ('nodes_size', ctypes.c_uint64), + ('contexts_offset', ctypes.c_uint64), + ('contexts_size', ctypes.c_uint64), + ('strings_offset', ctypes.c_uint64), + ('strings_size', ctypes.c_uint64) + ] + + class CalculateNodeOffsetVisitor: + def __init__(self): + self.current_offset = 0 + def visit(self, node): + node.node_offset = self.current_offset + + # Node data is followed by at least one ProvisionData object, with + # the only exception being root, which has no data by definition + self.current_offset += ctypes.sizeof(ProvisionNode) + self.current_offset += (ctypes.sizeof(ProvisionData) * + len(node.entries)) + + class SerializeVisitor: + def __init__(self, buffer): + self.buffer = buffer + + def visit(self, node): + # Node doesn't quite fit the C structure definition, so do this + # manually by using struct.pack + self.buffer.extend(struct.pack(' X-Patchwork-Id: 13491917 Received: from mail-ot1-f41.google.com (mail-ot1-f41.google.com [209.85.210.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C589E7FBA9 for ; Wed, 13 Dec 2023 21:52:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CI9kQBqU" Received: by mail-ot1-f41.google.com with SMTP id 46e09a7af769-6d9dbe224bbso5682810a34.2 for ; Wed, 13 Dec 2023 13:52:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1702504345; x=1703109145; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=AYKkK+f5zZdlnWTNXVtYDVOmQKapWxwmnn+Z7bX2oLI=; b=CI9kQBqUmXX79qETVwBHBjDOLD82Ea8y2FWVrlmvVOq1v9floLGCGRFJICugcQnMiJ lhoc4omNbBHa8RToqd5KoFLMnfWNB/FqjAlQuW/nodMKZwnD2a2gmY3spkXUfnjeIPN3 r8erdZvV2Qur/kKPKnp/M+NiEnkNMhxuUpekKqo4hitEAPjYACeda24jVtj88qaMyUMT f4tFRe9zTlhM4rJ93E6dfF61ZPmS8nv5J2fvebrzfI3rFIotfpjkNVq6XtdV+5Bqsa3d XXY4hVHnH6Zkhkwe3XqwzWPxejFwGau3TY9QkOy8DbgA8p0weVB7a50J2Of1c9lyiaxd mG4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702504345; x=1703109145; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=AYKkK+f5zZdlnWTNXVtYDVOmQKapWxwmnn+Z7bX2oLI=; b=oSO+PmYEgAiojf2pZKzgx9CjaZ3lUnyoCdO6332qYZx67ahR0iRQMLMj6qWG31ecGj Q0ijauF+zDIh95loUUwZw5H3ZwEuA6fgNE9pFU7TDym+1qFaxuyHfolynxwK+RPomxKQ 66S689JpRiKr0KRtSBxY7ldK52n8jQUypC/Uxz6K3NiX8jAimMWBvu6MG3GEFYvPMYZd +iuSjk1Y46I3yOfK1gftVk+HVmHhefAZUtKeOh5FwhkKwvQOY7xpzdGuhhPPAmnaxu/H TGLaz9m0YkQrS3BrcMq+M6LjYuLAey3RucvZ0cwANeBx3zPh1x0DYSDdHJmwSv1tdowB 2qNQ== X-Gm-Message-State: AOJu0YzTkl+9oHmtvLt2KFWUUlgxUSbnJqbLBIVTWsXbel/QJhUUXeV+ zjz28i2F00OaQZcMqQl0xH7EJjuvoYI= X-Google-Smtp-Source: AGHT+IHVlcBDCEr1kkGaszYrP0/97NouXlGNbZ9T7dw+J3Rtfq9/9BU/QcKFkpz8FtgamfMn25QQ7w== X-Received: by 2002:a05:6830:1655:b0:6d9:da23:9a27 with SMTP id h21-20020a056830165500b006d9da239a27mr8899572otr.68.1702504344832; Wed, 13 Dec 2023 13:52:24 -0800 (PST) Received: from localhost.localdomain (070-114-247-242.res.spectrum.com. [70.114.247.242]) by smtp.gmail.com with ESMTPSA id n18-20020a9d7412000000b006ce33ba6474sm2925612otk.4.2023.12.13.13.52.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Dec 2023 13:52:24 -0800 (PST) From: Denis Kenzior To: ofono@lists.linux.dev Cc: Denis Kenzior Subject: [PATCH 5/6] plugins: Support the new binary provisioning format Date: Wed, 13 Dec 2023 15:51:29 -0600 Message-ID: <20231213215132.287577-5-denkenz@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231213215132.287577-1-denkenz@gmail.com> References: <20231213215132.287577-1-denkenz@gmail.com> Precedence: bulk X-Mailing-List: ofono@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Binary provisioning format is based on a patricia trie / crit-bit tree, with each MCC + MNC combination represented by a single node. Each node contains an array of { SPN, offset } pairs, with the offset pointing to the region of memory where the context information resides. Each node also has two offsets, corresponding to left and right children of the node, as well as the position of the critical bit, based on which the tree is traversed. All strings are placed in the last section and are nil terminated. Structures are designed with 8-byte alignment and stored in little endian format, which will require no conversion for the vast majority of platforms in use. --- Makefile.am | 2 + plugins/provisiondb.c | 436 ++++++++++++++++++++++++++++++++++++++++++ plugins/provisiondb.h | 18 ++ 3 files changed, 456 insertions(+) create mode 100644 plugins/provisiondb.c create mode 100644 plugins/provisiondb.h diff --git a/Makefile.am b/Makefile.am index e7fd030f..11106162 100644 --- a/Makefile.am +++ b/Makefile.am @@ -656,6 +656,8 @@ endif if PROVISION builtin_sources += plugins/mbpi.h plugins/mbpi.c +builtin_sources += plugins/provisiondb.h plugins/provisiondb.c + builtin_modules += provision builtin_sources += plugins/provision.c diff --git a/plugins/provisiondb.c b/plugins/provisiondb.c new file mode 100644 index 00000000..d45166e2 --- /dev/null +++ b/plugins/provisiondb.c @@ -0,0 +1,436 @@ +/* + * oFono - Open Source Telephony + * Copyright (C) 2023 Cruise, LLC + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define OFONO_API_SUBJECT_TO_CHANGE +#include +#include + +#include "provisiondb.h" + +struct provision_header { + __le64 version; + __le64 file_size; + __le64 header_size; + __le64 node_struct_size; + __le64 provision_data_struct_size; + __le64 context_struct_size; + __le64 nodes_offset; + __le64 nodes_size; + __le64 contexts_offset; + __le64 contexts_size; + __le64 strings_offset; + __le64 strings_size; + + /* followed by nodes_size of node structures */ + /* followed by contexts_size of context structures */ + /* followed by strings_size packed strings */ +} __attribute__((packed)); + +struct node { + __le64 bit_offsets[2]; + __le32 mccmnc; + __le32 diff; /* Signed */ + __le64 provision_data_count; + /* followed by provision_data_count provision_data structures */ +} __attribute__((packed)); + +struct provision_data { + __le64 spn_offset; + __le64 context_offset; /* the offset contains count of contexts */ + /* followed by context structures */ +} __attribute__((packed)); + +struct context { + __le32 type; /* Corresponds to ofono_gprs_context_type bitmap */ + __le32 protocol; /* Corresponds to ofono_gprs_proto */ + __le32 authentication; /* Corresponds to ofono_gprs_auth_method */ + __le32 reserved; + __le64 name_offset; + __le64 apn_offset; + __le64 username_offset; + __le64 password_offset; + __le64 mmsproxy_offset; + __le64 mmsc_offset; +} __attribute__((packed)); + +struct provision_db { + int fd; + time_t mtime; + size_t size; + void *addr; + uint64_t nodes_offset; + uint64_t nodes_size; + uint64_t contexts_offset; + uint64_t contexts_size; + uint64_t strings_offset; + uint64_t strings_size; +}; + +struct provision_db *provision_db_new(const char *pathname) +{ + struct provision_header *hdr; + struct provision_db *pdb = NULL; + struct stat st; + void *addr; + size_t size; + int fd; + + if (!pathname) + return NULL; + + fd = open(pathname, O_RDONLY | O_CLOEXEC); + if (fd < 0) + return NULL; + + if (fstat(fd, &st) < 0) + goto error_close; + + size = st.st_size; + if (size < sizeof(struct provision_header)) + goto error_close; + + addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) + goto error_close; + + hdr = addr; + + if (L_LE64_TO_CPU(hdr->file_size) != size) + goto failed; + + if (L_LE64_TO_CPU(hdr->header_size) != sizeof(struct provision_header)) + goto failed; + + if (L_LE64_TO_CPU(hdr->node_struct_size) != sizeof(struct node)) + goto failed; + + if (L_LE64_TO_CPU(hdr->provision_data_struct_size) != + sizeof(struct provision_data)) + goto failed; + + if (L_LE64_TO_CPU(hdr->context_struct_size) != sizeof(struct context)) + goto failed; + + if (L_LE64_TO_CPU(hdr->header_size) + L_LE64_TO_CPU(hdr->nodes_size) + + L_LE64_TO_CPU(hdr->contexts_size) + + L_LE64_TO_CPU(hdr->strings_size) != size) + goto failed; + + pdb = l_new(struct provision_db, 1); + + pdb->fd = fd; + pdb->mtime = st.st_mtime; + pdb->size = size; + pdb->addr = addr; + pdb->nodes_offset = L_LE64_TO_CPU(hdr->nodes_offset); + pdb->nodes_size = L_LE64_TO_CPU(hdr->nodes_size); + pdb->contexts_offset = L_LE64_TO_CPU(hdr->contexts_offset); + pdb->contexts_size = L_LE64_TO_CPU(hdr->contexts_size); + pdb->strings_offset = L_LE64_TO_CPU(hdr->strings_offset); + pdb->strings_size = L_LE64_TO_CPU(hdr->strings_size); + + return pdb; + +failed: + munmap(addr, st.st_size); +error_close: + close(fd); + return NULL; +} + +struct provision_db *provision_db_new_default(void) +{ + struct provision_db *db = NULL; + size_t i; + const char * const paths[] = { "/usr/share/ofono/provision.db" }; + + for (i = 0; !db && i < L_ARRAY_SIZE(paths); i++) + db = provision_db_new(paths[i]); + + return db; +} + +void provision_db_free(struct provision_db *pdb) +{ + if (!pdb) + return; + + munmap(pdb->addr, pdb->size); + close(pdb->fd); + l_free(pdb); +} + +static int __get_node(struct provision_db *pdb, uint64_t offset, + struct node **out_node) +{ + uint64_t count; + struct node *node; + + if (offset + sizeof(struct node) > pdb->nodes_size) + return -EPROTO; + + node = pdb->addr + pdb->nodes_offset + offset; + offset += sizeof(struct node); + count = L_LE64_TO_CPU(node->provision_data_count); + + if (offset + count * sizeof(struct provision_data) > pdb->nodes_size) + return -EPROTO; + + *out_node = node; + return 0; +} + +static struct provision_data *__get_provision_data(struct node *node) +{ + return ((void *) node) + sizeof(struct node); +} + +static int __get_string(struct provision_db *pdb, uint64_t offset, + char **out_str) +{ + if (!offset) { + *out_str = NULL; + return 0; + } + + if (offset >= pdb->strings_size) + return -EPROTO; + + *out_str = pdb->addr + pdb->strings_offset + offset; + return 0; +} + +static int __get_contexts(struct provision_db *pdb, uint64_t offset, + struct ofono_gprs_provision_data **contexts, + size_t *n_contexts) +{ + void *start = pdb->addr + pdb->contexts_offset; + uint64_t num; + uint64_t i; + struct ofono_gprs_provision_data *ret; + int r; + + if (offset + sizeof(__le64) >= pdb->contexts_size) + return -EPROTO; + + num = l_get_le64(start + offset); + offset += sizeof(__le64); + + if (offset + num * sizeof(struct context) > pdb->contexts_size) + return -EPROTO; + + ret = l_new(struct ofono_gprs_provision_data, num); + + for (i = 0; i < num; i++, offset += sizeof(struct context)) { + struct context *context = start + offset; + + ret[i].type = L_LE32_TO_CPU(context->type); + ret[i].proto = L_LE32_TO_CPU(context->protocol); + ret[i].auth_method = L_LE32_TO_CPU(context->authentication); + + if ((r = __get_string(pdb, L_LE64_TO_CPU(context->name_offset), + &ret[i].name)) < 0) + goto fail; + + if ((r = __get_string(pdb, L_LE64_TO_CPU(context->apn_offset), + &ret[i].apn)) < 0) + goto fail; + + if ((r = __get_string(pdb, + L_LE64_TO_CPU(context->username_offset), + &ret[i].username)) < 0) + goto fail; + + if ((r = __get_string(pdb, + L_LE64_TO_CPU(context->password_offset), + &ret[i].password)) < 0) + goto fail; + + if ((r = __get_string(pdb, + L_LE64_TO_CPU(context->mmsproxy_offset), + &ret[i].message_proxy)) < 0) + goto fail; + + if ((r = __get_string(pdb, L_LE64_TO_CPU(context->mmsc_offset), + &ret[i].message_center)) < 0) + goto fail; + } + + *contexts = ret; + *n_contexts = num; + return 0; + +fail: + l_free(ret); + return r; +} + +static uint8_t choose(struct node *node, uint32_t key) +{ + return (key >> (31U - L_LE32_TO_CPU(node->diff))) & 1; +} + +static int __find(struct provision_db *pdb, uint32_t key, + struct node **out_node) +{ + struct node *child; + struct node *parent; + int r; + + r = __get_node(pdb, 0, &parent); + if (r < 0) + return r; + + r = __get_node(pdb, L_LE64_TO_CPU(parent->bit_offsets[0]), &child); + if (r < 0) + return r; + + while ((int32_t) L_LE32_TO_CPU(parent->diff) < + (int32_t) L_LE32_TO_CPU(child->diff)) { + uint8_t bit = choose(child, key); + uint64_t offset = L_LE64_TO_CPU(child->bit_offsets[bit]); + + parent = child; + + r = __get_node(pdb, offset, &child); + if (r < 0) + return r; + } + + if (L_LE32_TO_CPU(child->mccmnc) != key) + return -ENOENT; + + *out_node = child; + return 0; +} + +static int id_as_num(const char *id, size_t len) +{ + uint32_t v = 0; + size_t i; + + for (i = 0; i < len; i++) { + if (!l_ascii_isdigit(id[i])) + return -EINVAL; + + v = v * 10 + id[i] - '0'; + } + + return v; +} + +static int key_from_mcc_mnc(const char *mcc, const char *mnc, uint32_t *key) +{ + size_t mcc_len = strlen(mcc); + size_t mnc_len = strlen(mnc); + uint32_t v; + int r; + + if (mcc_len != 3) + return -EINVAL; + + if (mnc_len != 2 && mnc_len != 3) + return -EINVAL; + + r = id_as_num(mcc, mcc_len); + if (r < 0) + return r; + + v = r << 11; + + r = id_as_num(mnc, mnc_len); + if (r < 0) + return r; + + if (mnc_len == 3) + v |= 1 << 10; + + v |= r; + + *key = v; + return 0; +} + +int provision_db_lookup(struct provision_db *pdb, + const char *mcc, const char *mnc, const char *match_spn, + struct ofono_gprs_provision_data **items, + size_t *n_items) +{ + int r; + uint32_t key; + struct node *node; + struct provision_data *data; + struct provision_data *found = NULL; + uint64_t count; + uint64_t i; + + r = key_from_mcc_mnc(mcc, mnc, &key); + if (r < 0) + return r; + + /* + * Find the target node, then walk the provision_data items to + * match the spn. After that it is a matter of allocating the + * return contexts and copying over the details. + */ + + r = __find(pdb, key, &node); + if (r < 0) + return r; + + count = L_LE64_TO_CPU(node->provision_data_count); + data = __get_provision_data(node); + + if (!count) + return -ENOENT; + + /* + * provision_data objects are sorted by SPN, with no SPN (non-MVNO) + * being first. Since the provisioning data is imperfect, we try to + * match by SPN, but if that fails, we return the non-SPN entry, if + * present + */ + if (data[0].spn_offset == 0) { + found = data; + data += 1; + count -= 1; + } + + for (i = 0; i < count; i++) { + char *spn; + + r = __get_string(pdb, L_LE64_TO_CPU(data[i].spn_offset), &spn); + if (r < 0) + return r; + + if (l_streq0(spn, match_spn)) { + found = data + i; + break; + } + } + + if (!found) + return -ENOENT; + + return __get_contexts(pdb, L_LE64_TO_CPU(found->context_offset), + items, n_items); +} diff --git a/plugins/provisiondb.h b/plugins/provisiondb.h new file mode 100644 index 00000000..19a738fd --- /dev/null +++ b/plugins/provisiondb.h @@ -0,0 +1,18 @@ +/* + * oFono - Open Source Telephony + * Copyright (C) 2023 Cruise, LLC + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +struct ofono_gprs_provision_data; +struct provision_db; + +struct provision_db *provision_db_new(const char *pathname); +struct provision_db *provision_db_new_default(void); +void provision_db_free(struct provision_db *pdb); + +int provision_db_lookup(struct provision_db *pdb, + const char *mcc, const char *mnc, const char *spn, + struct ofono_gprs_provision_data **items, + size_t *n_items); From patchwork Wed Dec 13 21:51:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Denis Kenzior X-Patchwork-Id: 13491918 Received: from mail-ot1-f50.google.com (mail-ot1-f50.google.com [209.85.210.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3B75B7FBAC for ; Wed, 13 Dec 2023 21:52:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HzTxgVTZ" Received: by mail-ot1-f50.google.com with SMTP id 46e09a7af769-6d9e9b72ecfso4272440a34.3 for ; Wed, 13 Dec 2023 13:52:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1702504346; x=1703109146; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=X8pIFFMKDrzrTbrTZCdUIzNzKK8rf5+jgO2iT2IUKco=; b=HzTxgVTZjTBz48weSruA40qORJnLPNhpyy2uLyzwiqMV8TG6LXDxe59GbexHKObYct Wfckpnu1Zp2SVuBXrhjd9iSLjK+edXZe8AsAiX+0BRIiG/gLRayCa6SYzAY+RYAdIQOw bww/W5AhkQ9oQdHhbmm2Tsw893HnN3dbtgT2bvH3VCJrRFw16A3leFofx0nnGO1YHRrJ WW+Ztoep/GtdmMBgqYxQmgXBP8I47sFyDVMDwxw+XVRoYIzks8+aDiJYYB9+ZwA3z70f lLYUY5n8tAOK2d5az4K7yoocrcfi9iPJzW7SRqQuz1IJOm2wn3m9A1RtoEZKMCceMm8N 6SXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702504346; x=1703109146; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=X8pIFFMKDrzrTbrTZCdUIzNzKK8rf5+jgO2iT2IUKco=; b=dUariVi5pyWzn5bXWbpVOIafzjlLvhGo206LeyVPZ10UW29sBrm9SQ59OU/dQ+zfv+ LJHKe55Gl7SmzkxwUkCGgRTYITt9UW8dcwivwBIWxsN5VWQcHZ6ZNyj2eceZISsU+ZVH VSUPRGVyC+7tQX7Wi2f1FGP7xCEDrzof9+IvQy0vPCQXesNLJQMRrLXFK1SuIEBaslKw N2n99O82mVJbubf8cWK8K+FrepEktjZQOIuMswaFm2dA0HDga+3rOEpbZhY0dxOeF81l 62FYyP7g79KbU8vtqUksXowfTWVAh10Fqo+KEIxAlCWIUq3/F55/DHOurR738Yiw0Kpc Y1ew== X-Gm-Message-State: AOJu0YyC6BqDVCT86wjDLUyFinfMQw+IdwFpEhv9QMzC420oc3etOGyL laNa1vaHDF89s4E8/bdHRjxWC17LHOs= X-Google-Smtp-Source: AGHT+IEgSoHariiRIA9OLEua/KZ+f94YEJe8dilhVln5zkrU0C/VxcRXDX9GU/5UkotZ6SInAHCUtg== X-Received: by 2002:a05:6830:1d8b:b0:6d9:dda9:6044 with SMTP id y11-20020a0568301d8b00b006d9dda96044mr8860416oti.77.1702504346066; Wed, 13 Dec 2023 13:52:26 -0800 (PST) Received: from localhost.localdomain (070-114-247-242.res.spectrum.com. [70.114.247.242]) by smtp.gmail.com with ESMTPSA id n18-20020a9d7412000000b006ce33ba6474sm2925612otk.4.2023.12.13.13.52.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Dec 2023 13:52:25 -0800 (PST) From: Denis Kenzior To: ofono@lists.linux.dev Cc: Denis Kenzior Subject: [PATCH 6/6] tools: lookup-apn: Use the new provision_db utils Date: Wed, 13 Dec 2023 15:51:30 -0600 Message-ID: <20231213215132.287577-6-denkenz@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231213215132.287577-1-denkenz@gmail.com> References: <20231213215132.287577-1-denkenz@gmail.com> Precedence: bulk X-Mailing-List: ofono@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Migrate lookup-apn tool to use the new provision.db binary file format. The typical usage is now changed as follows: % tools/lookup-apn --file=sample.db 999 02 --- Makefile.am | 6 +- tools/lookup-apn.c | 173 ++++++++++++++++++++++++++------------------- 2 files changed, 103 insertions(+), 76 deletions(-) diff --git a/Makefile.am b/Makefile.am index 11106162..a61cc9f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -663,7 +663,6 @@ builtin_sources += plugins/provision.c builtin_modules += file_provision builtin_sources += plugins/file-provision.c - endif if MAINTAINER_MODE @@ -1010,8 +1009,9 @@ tools_auto_enable_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ tools_get_location_SOURCES = tools/get-location.c tools_get_location_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -tools_lookup_apn_SOURCES = plugins/mbpi.c plugins/mbpi.h tools/lookup-apn.c -tools_lookup_apn_LDADD = @GLIB_LIBS@ +tools_lookup_apn_SOURCES = plugins/provisiondb.h plugins/provisiondb.c \ + tools/lookup-apn.c +tools_lookup_apn_LDADD = $(ell_ldadd) tools_tty_redirector_SOURCES = tools/tty-redirector.c tools_tty_redirector_LDADD = @GLIB_LIBS@ diff --git a/tools/lookup-apn.c b/tools/lookup-apn.c index 884b32a0..ed6831af 100644 --- a/tools/lookup-apn.c +++ b/tools/lookup-apn.c @@ -1,115 +1,142 @@ /* - * * oFono - Open Source Telephony - * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. + * Copyright (C) 2023 Cruise, LLC * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA 02110-1301 USA - * + * SPDX-License-Identifier: GPL-2.0-only */ #ifdef HAVE_CONFIG_H #include #endif +#include #include +#include +#include -#include +#include #define OFONO_API_SUBJECT_TO_CHANGE #include #include -#include "plugins/mbpi.h" +#include "plugins/provisiondb.h" -static void lookup_apn(const char *match_mcc, const char *match_mnc, - gboolean allow_duplicates) -{ - GSList *l; - GSList *apns; - GError *error = NULL; +static const char *option_file = NULL; - g_print("Searching for info for network: %s%s\n", match_mcc, match_mnc); +static int lookup_apn(const char *match_mcc, const char *match_mnc, + const char *match_spn) +{ + struct provision_db *pdb; + struct ofono_gprs_provision_data *contexts; + size_t n_contexts; + int r; + size_t i; + + if (option_file) { + fprintf(stdout, "Opening database at: '%s'\n", option_file); + pdb = provision_db_new(option_file); + } else { + fprintf(stdout, "Opening database in default location\n"); + pdb = provision_db_new_default(); + } - apns = mbpi_lookup_apn(match_mcc, match_mnc, allow_duplicates, &error); + if (!pdb) { + fprintf(stdout, "Database opening failed\n"); + return -EIO; + } - if (apns == NULL) { - if (error != NULL) { - g_printerr("Lookup failed: %s\n", error->message); - g_error_free(error); - } + fprintf(stdout, "Searching for info for network: %s%s, spn: %s\n", + match_mcc, match_mnc, match_spn ? match_spn : ""); - return; + r = provision_db_lookup(pdb, match_mcc, match_mnc, match_spn, + &contexts, &n_contexts); + if (r < 0) { + fprintf(stderr, "Unable to lookup: %s\n", strerror(-r)); + return r; } - for (l = apns; l; l = l->next) { - struct ofono_gprs_provision_data *ap = l->data; + for (i = 0; i < n_contexts; i++) { + struct ofono_gprs_provision_data *ap = contexts + i; + + fprintf(stdout, "\nName: %s\n", ap->name); + fprintf(stdout, "APN: %s\n", ap->apn); + fprintf(stdout, "Type: %x\n", ap->type); + fprintf(stdout, "Proto: %x\n", ap->proto); - g_print("\n"); - g_print("Name: %s\n", ap->name); - g_print("APN: %s\n", ap->apn); - g_print("Type: %s\n", mbpi_ap_type(ap->type)); - g_print("Username: %s\n", ap->username); - g_print("Password: %s\n", ap->password); + if (ap->username) + fprintf(stdout, "Username: %s\n", ap->username); - mbpi_ap_free(ap); + if (ap->password) + fprintf(stdout, "Password: %s\n", ap->password); + + if (ap->type & OFONO_GPRS_CONTEXT_TYPE_MMS) { + if (ap->message_proxy) + fprintf(stdout, "Message Proxy: %s\n", + ap->message_proxy); + fprintf(stdout, "Message Center: %s\n", + ap->message_center); + } } - g_slist_free(apns); + l_free(contexts); + + provision_db_free(pdb); + + return 0; } -static gboolean option_version = FALSE; -static gboolean option_duplicates = FALSE; +static void usage(void) +{ + printf("lookup-apn\nUsage:\n"); + printf("lookup-apn [options] [spn]\n"); + printf("Options:\n" + "\t-v, --version Show version\n" + "\t-f, --file Provision DB file to use\n" + "\t-h, --help Show help options\n"); +} -static GOptionEntry options[] = { - { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, - "Show version information and exit" }, - { "allow-duplicates", 0, 0, G_OPTION_ARG_NONE, &option_duplicates, - "Allow duplicate access point types" }, - { NULL }, +static const struct option options[] = { + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { "file", required_argument, NULL, 'f' }, + { }, }; int main(int argc, char **argv) { - GOptionContext *context; - GError *error = NULL; - - context = g_option_context_new(NULL); - g_option_context_add_main_entries(context, options, NULL); - - if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { - if (error != NULL) { - g_printerr("%s\n", error->message); - g_error_free(error); - } else - g_printerr("An unknown error occurred\n"); - exit(1); + for (;;) { + int opt = getopt_long(argc, argv, "f:vh", options, NULL); + + if (opt < 0) + break; + + switch (opt) { + case 'f': + option_file = optarg; + break; + case 'v': + printf("%s\n", VERSION); + return EXIT_SUCCESS; + case 'h': + usage(); + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; + } } - g_option_context_free(context); - - if (option_version == TRUE) { - g_print("%s\n", VERSION); - exit(0); + if (argc - optind > 3) { + fprintf(stderr, "Invalid command line parameters\n"); + return EXIT_FAILURE; } - if (argc < 2) { - g_printerr("Missing parameters\n"); - exit(1); + if (argc - optind < 2) { + fprintf(stderr, "Missing MCC MNC parameters\n"); + return EXIT_FAILURE; } - lookup_apn(argv[1], argv[2], option_duplicates); - - return 0; + return lookup_apn(argv[optind], argv[optind + 1], + argc - optind == 3 ? argv[optind + 2] : NULL); }