rule2sor 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .DS_Store
4
+ .venv/
5
+ dist/
6
+ *.egg-info/
@@ -0,0 +1,29 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Debug rule2sor (inner.json)",
6
+ "type": "debugpy",
7
+ "request": "launch",
8
+ "module": "rule2sor.cli",
9
+ "python": "${workspaceFolder}/.venv/bin/python",
10
+ "args": [
11
+ "${workspaceFolder}/../oscore/inner.json"
12
+ ],
13
+ "cwd": "${workspaceFolder}",
14
+ "console": "integratedTerminal"
15
+ },
16
+ {
17
+ "name": "Debug rule2sor (active file)",
18
+ "type": "debugpy",
19
+ "request": "launch",
20
+ "module": "rule2sor.cli",
21
+ "python": "${workspaceFolder}/.venv/bin/python",
22
+ "args": [
23
+ "${file}"
24
+ ],
25
+ "cwd": "${workspaceFolder}",
26
+ "console": "integratedTerminal"
27
+ }
28
+ ]
29
+ }
rule2sor-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Laurent Toutain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,65 @@
1
+ Metadata-Version: 2.4
2
+ Name: rule2sor
3
+ Version: 0.1.0
4
+ Summary: Convert OpenSCHC JSON rule files into CORECONF/CBOR .sor files
5
+ Project-URL: Homepage, https://github.com/ltn22/rule2sor
6
+ Author-email: Laurent Toutain <laurent.toutain@gmail.com>
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.9
10
+ Requires-Dist: cbor-diag
11
+ Requires-Dist: cbor2
12
+ Requires-Dist: pycoreconf>=0.3
13
+ Description-Content-Type: text/markdown
14
+
15
+ # rule2sor
16
+
17
+ Convert [OpenSCHC](https://github.com/ltn22/openschc) JSON rule files into
18
+ `.sor` files (the Set of Rules serialized in CORECONF/CBOR, following the
19
+ ietf-schc YANG data model).
20
+
21
+ ## Usage with uvx
22
+
23
+ uvx --from git+https://github.com/ltn22/rule2sor rule2sor <rule-file.json>
24
+
25
+ Or after installation (`pip install git+https://github.com/ltn22/rule2sor`):
26
+
27
+ rule2sor <rule-file.json> [-s <sid-file>] [-o <output.sor>] [-q]
28
+
29
+ By default the output file has the same name as the input with the `.sor`
30
+ extension, and the SID file is the bundled `ietf-schc@2026-05-07.sid`.
31
+ With `-q` only the final message is printed.
32
+
33
+ If you pass a `.sor` file instead of a JSON file, `rule2sor` will read and print
34
+ its contents (CBOR hex, CBOR diagnostic notation, and decoded RESTCONF JSON)
35
+ directly to the standard output:
36
+
37
+ rule2sor <file.sor> [-s <sid-file>]
38
+
39
+ Example:
40
+
41
+ rule2sor atmos41.json
42
+ # -> atmos41.sor
43
+
44
+ rule2sor atmos41.sor
45
+ # displays the content of atmos41.sor
46
+
47
+ ## What it does
48
+
49
+ This is an extraction of the minimal code from OpenSCHC:
50
+
51
+ - `gen_rulemanager.py` — trimmed `RuleManager` keeping:
52
+ - `Add()`: loads a JSON rule file, checks rule integrity (RuleID overlaps,
53
+ valid FID/MO/CDA, one of compression/fragmentation/no-compression per
54
+ rule) and fills in missing defaults (FL, FP, DI);
55
+ - `Print()`: displays the rules as ASCII tables;
56
+ - `add_sid_file()` / `to_coreconf()`: serializes the Set of Rules in
57
+ CORECONF/CBOR using the SID values.
58
+ - `gen_parameters.py` — constants, `YANG_ID` mapping (simplified to a direct
59
+ name-to-YANG-identifier mapping) and `adapt_value()`.
60
+ - `ietf-schc@2026-05-07.sid` — default SID file.
61
+
62
+ ## JSON rule format
63
+
64
+ See the long docstring at the top of `rule2sor/gen_rulemanager.py`, which
65
+ documents the OpenSCHC JSON rule data model (compression and fragmentation).
@@ -0,0 +1,51 @@
1
+ # rule2sor
2
+
3
+ Convert [OpenSCHC](https://github.com/ltn22/openschc) JSON rule files into
4
+ `.sor` files (the Set of Rules serialized in CORECONF/CBOR, following the
5
+ ietf-schc YANG data model).
6
+
7
+ ## Usage with uvx
8
+
9
+ uvx --from git+https://github.com/ltn22/rule2sor rule2sor <rule-file.json>
10
+
11
+ Or after installation (`pip install git+https://github.com/ltn22/rule2sor`):
12
+
13
+ rule2sor <rule-file.json> [-s <sid-file>] [-o <output.sor>] [-q]
14
+
15
+ By default the output file has the same name as the input with the `.sor`
16
+ extension, and the SID file is the bundled `ietf-schc@2026-05-07.sid`.
17
+ With `-q` only the final message is printed.
18
+
19
+ If you pass a `.sor` file instead of a JSON file, `rule2sor` will read and print
20
+ its contents (CBOR hex, CBOR diagnostic notation, and decoded RESTCONF JSON)
21
+ directly to the standard output:
22
+
23
+ rule2sor <file.sor> [-s <sid-file>]
24
+
25
+ Example:
26
+
27
+ rule2sor atmos41.json
28
+ # -> atmos41.sor
29
+
30
+ rule2sor atmos41.sor
31
+ # displays the content of atmos41.sor
32
+
33
+ ## What it does
34
+
35
+ This is an extraction of the minimal code from OpenSCHC:
36
+
37
+ - `gen_rulemanager.py` — trimmed `RuleManager` keeping:
38
+ - `Add()`: loads a JSON rule file, checks rule integrity (RuleID overlaps,
39
+ valid FID/MO/CDA, one of compression/fragmentation/no-compression per
40
+ rule) and fills in missing defaults (FL, FP, DI);
41
+ - `Print()`: displays the rules as ASCII tables;
42
+ - `add_sid_file()` / `to_coreconf()`: serializes the Set of Rules in
43
+ CORECONF/CBOR using the SID values.
44
+ - `gen_parameters.py` — constants, `YANG_ID` mapping (simplified to a direct
45
+ name-to-YANG-identifier mapping) and `adapt_value()`.
46
+ - `ietf-schc@2026-05-07.sid` — default SID file.
47
+
48
+ ## JSON rule format
49
+
50
+ See the long docstring at the top of `rule2sor/gen_rulemanager.py`, which
51
+ documents the OpenSCHC JSON rule data model (compression and fragmentation).
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "rule2sor"
7
+ version = "0.1.0"
8
+ description = "Convert OpenSCHC JSON rule files into CORECONF/CBOR .sor files"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Laurent Toutain", email = "laurent.toutain@gmail.com" }]
13
+ dependencies = ["cbor2", "cbor-diag", "pycoreconf>=0.3"]
14
+
15
+ [project.urls]
16
+ Homepage = "https://github.com/ltn22/rule2sor"
17
+
18
+ [project.scripts]
19
+ rule2sor = "rule2sor.cli:main"
20
+
21
+ [tool.hatch.build.targets.wheel]
22
+ packages = ["rule2sor"]
@@ -0,0 +1,3 @@
1
+ from .gen_rulemanager import RuleManager
2
+
3
+ __all__ = ["RuleManager"]
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ rule2sor: convert an OpenSCHC JSON rule file into a .sor file
4
+ (CORECONF/CBOR representation of the Set of Rules).
5
+
6
+ Usage:
7
+ rule2sor <rule-file.json> [-s <sid-file>] [-o <output.sor>] [-q]
8
+
9
+ The rules are validated and completed by RuleManager.Add(), then
10
+ serialized with RuleManager.to_coreconf().
11
+ """
12
+
13
+ import argparse
14
+ import binascii
15
+ import json
16
+ import os
17
+ import sys
18
+
19
+ from cbor_diag import cbor2diag
20
+ from pycoreconf import CORECONFModel
21
+
22
+ from .gen_rulemanager import RuleManager
23
+
24
+ DEFAULT_SID_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)),
25
+ "ietf-schc@2026-05-07.sid")
26
+
27
+ def main():
28
+ parser = argparse.ArgumentParser(
29
+ description="Convert OpenSCHC JSON rules to a CORECONF/CBOR .sor file, or display a .sor file")
30
+ parser.add_argument("rule_file", help="JSON file describing the set of rules, or .sor file to display")
31
+ parser.add_argument("-s", "--sid", default=DEFAULT_SID_FILE,
32
+ help="SID file (default: %(default)s)")
33
+ parser.add_argument("-o", "--output",
34
+ help="output .sor file (default: rule file with .sor extension)")
35
+ parser.add_argument("-q", "--quiet", action="store_true",
36
+ help="do not print the rules nor the CBOR dump")
37
+ args = parser.parse_args()
38
+
39
+ if args.rule_file.endswith(".sor"):
40
+ if not os.path.exists(args.rule_file):
41
+ print(f"Error: file not found: {args.rule_file}", file=sys.stderr)
42
+ sys.exit(1)
43
+ with open(args.rule_file, "rb") as f:
44
+ ycbor = f.read()
45
+ print("CBOR (hex):", binascii.hexlify(ycbor).decode())
46
+ print("\nCBOR diagnostic notation:")
47
+ print(cbor2diag(ycbor))
48
+ print("\nRESTCONF JSON:")
49
+ model = CORECONFModel([args.sid])
50
+ print(json.dumps(model.decode(ycbor, as_rfc7951=True), indent=2))
51
+ return
52
+
53
+ output = args.output
54
+ if output is None:
55
+ base, _ = os.path.splitext(args.rule_file)
56
+ output = base + ".sor"
57
+
58
+ rm = RuleManager()
59
+ rm.Add(file=args.rule_file, device="test:device1")
60
+ rm.add_sid_file(args.sid)
61
+
62
+ if not args.quiet:
63
+ rm.Print()
64
+
65
+ ycbor = rm.to_coreconf()
66
+ if not args.quiet:
67
+ print(binascii.hexlify(ycbor))
68
+ print("\nCBOR diagnostic notation:")
69
+ print(cbor2diag(ycbor))
70
+ print("\nRESTCONF JSON:")
71
+ model = CORECONFModel([args.sid])
72
+ print(json.dumps(model.decode(ycbor, as_rfc7951=True), indent=2))
73
+
74
+ with open(output, "wb") as f:
75
+ f.write(ycbor)
76
+
77
+ print("{} written ({} bytes)".format(output, len(ycbor)))
78
+
79
+ if __name__ == "__main__":
80
+ sys.exit(main())
@@ -0,0 +1,318 @@
1
+ T_RULEID = "RuleID"
2
+ T_RULEIDVALUE = "RuleIDValue"
3
+ T_RULEIDLENGTH = "RuleIDLength"
4
+ T_DEVICEID = "DeviceID"
5
+
6
+ T_FID = "FID"
7
+ T_FL = "FL"
8
+ T_FP = "FP"
9
+ T_DI = "DI"
10
+ T_TV = "TV"
11
+ T_TV_IND = "TV.IND"
12
+ T_MO = "MO"
13
+ T_MO_VAL = "MO.VAL"
14
+ T_CDA = "CDA"
15
+ T_SB = "SB"
16
+
17
+ T_PROTO_IPV6 = "IPV6"
18
+ # IPv6 header fields
19
+ T_IPV6_VER = "IPV6.VER"
20
+ T_IPV6_TC = "IPV6.TC"
21
+ T_IPV6_FL = "IPV6.FL"
22
+ T_IPV6_LEN = "IPV6.LEN"
23
+ T_IPV6_NXT = "IPV6.NXT"
24
+ T_IPV6_HOP_LMT = "IPV6.HOP_LMT"
25
+ T_IPV6_DEV_PREFIX = "IPV6.DEV_PREFIX"
26
+ T_IPV6_DEV_IID = "IPV6.DEV_IID"
27
+ T_IPV6_APP_PREFIX = "IPV6.APP_PREFIX"
28
+ T_IPV6_APP_IID = "IPV6.APP_IID"
29
+
30
+ T_IPV4_VER = "IPV4.VER"
31
+
32
+ T_PROTO_ICMPV6 = "ICMPV6"
33
+ # ICMPv6 header fields
34
+ T_ICMPV6_TYPE = "ICMPV6.TYPE"
35
+ T_ICMPV6_CODE = "ICMPV6.CODE"
36
+ T_ICMPV6_CKSUM = "ICMPV6.CKSUM"
37
+ T_ICMPV6_IDENT = "ICMPV6.IDENT"
38
+ T_ICMPV6_SEQNO = "ICMPV6.SEQNO"
39
+ T_ICMPV6_UNUSED = "ICMPV6.UNUSED"
40
+ T_ICMPV6_PAYLOAD = "ICMPV6.PAYLOAD"
41
+ # ICMPv6 types
42
+ T_ICMPV6_TYPE_ECHO_REQUEST = "ICMPV6.TYPE.ECHO.REQUEST"
43
+ T_ICMPV6_TYPE_ECHO_REPLY = "ICMPV6.TYPE.ECHO.REPLY"
44
+
45
+ T_PROTO_UDP = "UDP"
46
+ # UDP fields
47
+ T_UDP_DEV_PORT = "UDP.DEV_PORT"
48
+ T_UDP_APP_PORT = "UDP.APP_PORT"
49
+ T_UDP_LEN = "UDP.LEN"
50
+ T_UDP_CKSUM = "UDP.CKSUM"
51
+
52
+
53
+
54
+ T_PROTO_COAP = "COAP"
55
+ # CoAP fields
56
+ T_COAP_VERSION = "COAP.VER"
57
+ T_COAP_TYPE = "COAP.TYPE"
58
+ T_COAP_TKL = "COAP.TKL"
59
+ T_COAP_CODE = "COAP.CODE"
60
+ T_COAP_MID = "COAP.MID"
61
+ T_COAP_TOKEN = "COAP.TOKEN"
62
+ # CoAP options string (written exactly as in standards)
63
+ T_COAP_OPT_IF_MATCH = "COAP.IF-MATCH"
64
+ T_COAP_OPT_URI_HOST = "COAP.URI-HOST"
65
+ T_COAP_OPT_ETAG = "COAP.ETAG"
66
+ T_COAP_OPT_IF_NONE_MATCH = "COAP.IF-NONE-MATCH"
67
+ T_COAP_OPT_OBS = "COAP.OBSERVE"
68
+ T_COAP_OPT_URI_PORT = "COAP.URI-PORT"
69
+ T_COAP_OPT_LOC_PATH = "COAP.LOCATION-PATH"
70
+ T_COAP_OPT_URI_PATH = "COAP.URI-PATH"
71
+ T_COAP_OPT_CONT_FORMAT = "COAP.CONTENT-FORMAT"
72
+ T_COAP_OPT_MAX_AGE = "COAP.MAX-AGE"
73
+ T_COAP_OPT_URI_QUERY = "COAP.URI-QUERY"
74
+ T_COAP_OPT_ACCEPT = "COAP.ACCEPT"
75
+ T_COAP_OPT_LOC_QUERY = "COAP.LOCATION-QUERY"
76
+ T_COAP_OPT_BLOCK2 = "COAP.BLOCK2"
77
+ T_COAP_OPT_BLOCK1 = "COAP.BLOCK1"
78
+ T_COAP_OPT_SIZE2 = "COAP.SIZE2"
79
+ T_COAP_OPT_PROXY_URI = "COAP.PROXY-URI"
80
+ T_COAP_OPT_PROXY_SCHEME = "COAP.PROXY-SCHEME"
81
+ T_COAP_OPT_SIZE1 = "COAP.SIZE1"
82
+ T_COAP_OPT_NO_RESP = "COAP.NO-RESPONSE"
83
+
84
+ T_OSCORE_FLAGS = "COAP.OSCORE_FLAGS"
85
+ T_OSCORE_FLAGS_FLAGS = "COAP.OSCORE_FLAGS_FLAGS"
86
+ T_OSCORE_FLAGS_N = "COAP.OSCORE_FLAGS_N"
87
+ T_OSCORE_KID = "COAP.OSCORE_KID"
88
+ T_OSCORE_KIDCTX = "COAP.OSCORE_KID_CTX"
89
+ T_OSCORE_PIV = "COAP.OSCORE_PIV"
90
+ T_COAP_OPT_END = "COAP.End"
91
+
92
+ T_UNUSED = "UNUSED"
93
+ T_PAYLOAD = "PAYLOAD"
94
+
95
+ T_FUNCTION_VAR = "var"
96
+ T_FUNCTION_TKL = "tkl"
97
+
98
+ T_FUNCTION_VAR = "var"
99
+ T_FUNCTION_VARBIT = "var_bit"
100
+ T_FUNCTION_TKL = "tkl"
101
+
102
+
103
+ T_DIR_UP = "UP"
104
+ T_DIR_DW = "DW"
105
+ T_DIR_BI = "BI"
106
+
107
+ T_MO_EQUAL = "EQUAL"
108
+ T_MO_IGNORE = "IGNORE"
109
+ T_MO_MSB = "MSB"
110
+ T_MO_MMAP = "MATCH-MAPPING"
111
+ T_MO_MATCH_REV_RULE = "REV-RULE-MATCH"
112
+
113
+ T_CDA_NOT_SENT = "NOT-SENT"
114
+ T_CDA_VAL_SENT = "VALUE-SENT"
115
+ T_CDA_MAP_SENT = "MAPPING-SENT"
116
+ T_CDA_LSB = "LSB"
117
+ T_CDA_COMP_LEN = "COMPUTE-LENGTH"
118
+ T_CDA_COMP_CKSUM = "COMPUTE-CHECKSUM"
119
+ T_CDA_DEVIID = "DEVIID"
120
+ T_CDA_APPIID = "APPIID"
121
+ T_CDA_LORA_DEVIID = "COMPUTE-DEVIID"
122
+ T_CDA_REV_COMPRESS = "REV-COMPRESSED-SENT"
123
+
124
+ T_ALGO = "ALGO"
125
+ T_ALGO_DIRECT = "DIRECT"
126
+ T_ALGO_COAP_OPT = "COAP_OPTION"
127
+
128
+ T_META = "META"
129
+ T_UP_RULES = "UP_RULES"
130
+ T_DW_RULES = "DW_RULES"
131
+ T_LAST_USED = "LAST_USED"
132
+
133
+ T_ACTION = "Action"
134
+
135
+ T_COMP = "Compression"
136
+ T_NO_COMP = "NoCompression"
137
+ T_FRAG = "Fragmentation"
138
+ T_FRAG_MODE = "FRMode"
139
+ T_FRAG_NO_ACK = "NoAck" #YANG no-ack
140
+ T_FRAG_ACK_ALWAYS = "AckAlways"
141
+ T_FRAG_ACK_ON_ERROR = "AckOnError"
142
+ T_FRAG_DIRECTION = "FRDirection" #YANG direction
143
+ T_FRAG_PROF = "FRModeProfile"
144
+ T_FRAG_DTAG_SIZE = "dtagSize" #YANG dtag-size
145
+ T_FRAG_W_SIZE = "WSize" #YANG w-size
146
+ T_FRAG_FCN = "FCNSize" #YANG fcn-size
147
+ T_FRAG_WINDOW_SIZE = "windowSize" #YANG window-size
148
+ T_MAX_INTER_FRAME = "MaxInterFrame" #YANG max-interleaved-frames
149
+ T_FRAG_ACK_BEHAVIOR = "ackBehavior" #YANG ack-behavior
150
+ T_FRAG_AFTER_ALL1 = "afterAll1"
151
+ T_FRAG_AFTER_ALL0 = "afterAll0"
152
+ T_FRAG_AFTER_ANY = "afterAny"
153
+ T_FRAG_TILE = "tileSize" #YANG tile-size
154
+ T_FRAG_MIC = "MICALgorithm" #YANG rcs-algorithm
155
+ T_MAX_PACKET_SIZE = "MaxPcktSize" #YANG maximum-packet-size
156
+ T_MAX_INTER_FRAME = "MaxInterFrame" #YANG max-interleaved-frames
157
+ T_FRAG_MAX_RETRY = "maxRetry" #YANG max-ack-requests
158
+ T_FRAG_TIMEOUT = "timeout" #YANG retransmission-timer
159
+ T_FRAG_L2WORDSIZE = "L2WordSize" #YANG l2-word-size
160
+ T_FRAG_LAST_TILE_IN_ALL1 = "lastTileInAll1" #YANG tile-in-all-1
161
+ T_FRAG_RFC8724 = "RCS_CRC32"
162
+
163
+ T_POSITION_CORE = "core"
164
+ T_POSITION_DEVICE = "device"
165
+
166
+ T_INDEXES = "Indexes"
167
+ T_CMD_INDIRECT = "INDIRECT"
168
+
169
+ T_O_DELTA = "COAP.O-DELTA"
170
+ T_O_LENGTH = "COAP.O-LENGTH"
171
+ T_O_VALUE = "COAP.O-VALUE"
172
+
173
+
174
+ YANG_ID = {
175
+ "module": "ietf-schc",
176
+ T_CDA_APPIID: "cda-appiid",
177
+ T_CDA_COMP_CKSUM: "cda-compute",
178
+ T_CDA_COMP_LEN: "cda-compute",
179
+ T_CDA_DEVIID: "cda-deviid",
180
+ T_CDA_LSB: "cda-lsb",
181
+ T_CDA_MAP_SENT: "cda-mapping-sent",
182
+ T_CDA_NOT_SENT: "cda-not-sent",
183
+ T_CDA_VAL_SENT: "cda-value-sent",
184
+ T_DIR_BI: "di-bidirectional",
185
+ T_DIR_DW: "di-down",
186
+ T_DIR_UP: "di-up",
187
+ T_COAP_CODE: "fid-coap-code",
188
+ T_COAP_MID: "fid-coap-mid",
189
+ T_OSCORE_FLAGS: "fid-coap-option-oscore-flags",
190
+ T_OSCORE_FLAGS_FLAGS: "fid-coap-option-oscore-flags-flags",
191
+ T_OSCORE_FLAGS_N: "fid-coap-option-oscore-flags-n",
192
+ T_OSCORE_KID: "fid-coap-option-oscore-kid",
193
+ T_OSCORE_KIDCTX: "fid-coap-option-oscore-kidctx",
194
+ T_OSCORE_PIV: "fid-coap-option-oscore-piv",
195
+ T_COAP_TKL: "fid-coap-tkl",
196
+ T_COAP_TOKEN: "fid-coap-token",
197
+ T_COAP_TYPE: "fid-coap-type",
198
+ T_COAP_VERSION: "fid-coap-version",
199
+ T_IPV6_APP_IID: "fid-ipv6-appiid",
200
+ T_IPV6_APP_PREFIX: "fid-ipv6-appprefix",
201
+ T_IPV6_DEV_IID: "fid-ipv6-deviid",
202
+ T_IPV6_DEV_PREFIX: "fid-ipv6-devprefix",
203
+ T_IPV6_FL: "fid-ipv6-flowlabel",
204
+ T_IPV6_HOP_LMT: "fid-ipv6-hoplimit",
205
+ T_IPV6_NXT: "fid-ipv6-nextheader",
206
+ T_IPV6_LEN: "fid-ipv6-payload-length",
207
+ T_IPV6_TC: "fid-ipv6-trafficclass",
208
+ T_IPV6_VER: "fid-ipv6-version",
209
+ T_IPV4_VER: "fid-ipv6-version",
210
+ T_UDP_APP_PORT: "fid-udp-app-port",
211
+ T_UDP_CKSUM: "fid-udp-checksum",
212
+ T_UDP_DEV_PORT: "fid-udp-dev-port",
213
+ T_UDP_LEN: "fid-udp-length",
214
+ T_FUNCTION_TKL: "fl-token-length",
215
+ T_FUNCTION_VAR: "fl-variable",
216
+ T_FUNCTION_VARBIT: "fl-variable-bit",
217
+ T_FRAG_ACK_ALWAYS: "fragmentation-mode-ack-always",
218
+ T_FRAG_ACK_ON_ERROR: "fragmentation-mode-ack-on-error",
219
+ T_FRAG_NO_ACK: "fragmentation-mode-no-ack",
220
+ T_MO_EQUAL: "mo-equal",
221
+ T_MO_IGNORE: "mo-ignore",
222
+ T_MO_MMAP: "mo-match-mapping",
223
+ T_MO_MSB: "mo-msb",
224
+ T_FRAG_RFC8724: "rcs-crc32",
225
+ # from OAM
226
+ T_ICMPV6_CODE: "fid-icmpv6-code",
227
+ T_ICMPV6_TYPE: "fid-icmpv6-type",
228
+ T_ICMPV6_IDENT: "fid-icmpv6-identifier",
229
+ T_ICMPV6_SEQNO: "fid-icmpv6-sequence",
230
+ T_ICMPV6_CKSUM: "fid-icmpv6-checksum",
231
+ T_ICMPV6_PAYLOAD: "fid-icmpv6-payload",
232
+ # for quentin extension
233
+ T_O_DELTA: "fid-coap-delta-types",
234
+ T_O_LENGTH: "fid-coap-length",
235
+ T_O_VALUE: "fid-coap-value",
236
+
237
+ T_UNUSED: "fid-unused",
238
+ T_PAYLOAD: "fid-payload",
239
+ }
240
+
241
+ COAP_OPTION_NUMBERS = {
242
+ T_COAP_OPT_IF_MATCH: 1,
243
+ T_COAP_OPT_URI_HOST: 3,
244
+ T_COAP_OPT_ETAG: 4,
245
+ T_COAP_OPT_IF_NONE_MATCH: 5,
246
+ T_COAP_OPT_OBS: 6,
247
+ T_COAP_OPT_URI_PORT: 7,
248
+ T_COAP_OPT_LOC_PATH: 8,
249
+ T_COAP_OPT_URI_PATH: 11,
250
+ T_COAP_OPT_CONT_FORMAT: 12,
251
+ T_COAP_OPT_MAX_AGE: 14,
252
+ T_COAP_OPT_URI_QUERY: 15,
253
+ T_COAP_OPT_ACCEPT: 17,
254
+ T_COAP_OPT_LOC_QUERY: 20,
255
+ T_COAP_OPT_BLOCK2: 23,
256
+ T_COAP_OPT_BLOCK1: 27,
257
+ T_COAP_OPT_SIZE2: 28,
258
+ T_COAP_OPT_PROXY_URI: 35,
259
+ T_COAP_OPT_PROXY_SCHEME: 39,
260
+ T_COAP_OPT_SIZE1: 60,
261
+ T_COAP_OPT_NO_RESP: 258,
262
+ }
263
+
264
+ import ipaddress
265
+
266
+ def adapt_value(value, length=None, FID=None):
267
+ """transform any value of any type in the smallest bytearray.
268
+ FID allows to convert properly the string to IPv6 address."""
269
+
270
+ if type(value) is list:
271
+ result = []
272
+ for e in value:
273
+ result.append(adapt_value(e, length, FID))
274
+
275
+ return value
276
+
277
+ if type(value) is int:
278
+
279
+ if FID in [T_IPV6_APP_IID, T_IPV6_APP_PREFIX, T_IPV6_DEV_IID, T_IPV6_DEV_PREFIX] and length != None:
280
+ return value.to_bytes(length//8, byteorder='big')
281
+
282
+ size = 0
283
+ v = value
284
+ while v != 0:
285
+ v = v >> 8
286
+ size += 1
287
+
288
+ if size == 0: # when value == 0 store 0
289
+ size=1
290
+
291
+ return value.to_bytes (size, byteorder='big')
292
+ if type(value) is str:
293
+ if FID in [T_IPV6_APP_IID, T_IPV6_APP_PREFIX, T_IPV6_DEV_IID, T_IPV6_DEV_PREFIX]:
294
+ # string has to be converted as IPv6 addresses
295
+
296
+ slash_pos = value.find("/")
297
+ if slash_pos != -1:
298
+ # a prefix is given, remove / to be compatible with ip_address
299
+ value = value[:slash_pos]
300
+
301
+ addr = ipaddress.ip_address(value)
302
+ if addr.version != 6: # expect an IPv6 address
303
+ raise ValueError ("only IPv6 is supported, can not support {}".format(addr.version))
304
+
305
+ if FID in [T_IPV6_DEV_PREFIX, T_IPV6_APP_PREFIX]: #prefix top 8
306
+ return addr.packed[:8]
307
+ elif FID in [T_IPV6_DEV_IID, T_IPV6_APP_IID]: #IID bottom 8
308
+ return addr.packed[8:]
309
+ else:
310
+ raise ValueError ("{} Fid not found".format(FID))
311
+ else: # a regular string
312
+ return value.encode()
313
+ elif type(value) is bytes:
314
+ return value
315
+ elif value == None:
316
+ return None
317
+ else:
318
+ raise ValueError("Unknown type", type(value))