Commit Graph

4775 Commits

Author SHA1 Message Date
Harald Welte a9c91cc0a1 libosmogsm: Ensure MILENAGE + XOR-3G K length is 128 bit
Since Change-Id Ie775fedba4a3fa12314c0f7c8a369662ef6a40df we are
supporting K-lengths != 128 bit.  However, our existing MILENAGE
and XOR-3G algorithms only support that key length, so let's add
some explicit checks for that.

Change-Id: Iae8b93cf059abda087101cdd01bbcf92d355753b
2023-06-02 08:29:55 +00:00
Harald Welte 5248c47e1f libosmogsm: Add OSMO_ASSERT() to ensure correct algorithm
Let's make sure that nobody ever ends up calling the algo_impl
call-backs with data of a non-matching algorithm.  This should
never happen at all, as all normal users should go through
the auth_core.c:osmo_auth_gen_vec* API, which dispatches based
on algorithm.

Change-Id: I22b504b6cffb4999b2f14772fffcb2f6f02c198c
2023-06-02 08:29:55 +00:00
Harald Welte 08450c9ec6 libosmogsm: Support authentication with 256-bit K and/or OP/OPc
3GPP TS 33.102 Section 6.3.7 states that K can be 128 or 256 bits,
while our 'struct osmo_sub_auth_data' had a fixed-size 128bit field.

This means we cannot use our auth_core for algorithms with larger
key sizes, such as TUAK.  Let's introduce osmo_sub_auth_data2 for
larger (and variable) sized K and OP[c].

K and OP[c] can even have different sizes in TUAK, where OP[c] is
always 256bit, but K can be 128 or 256 bits.  So we need separate
length fields for K and OP[c].

I'm adding backwards-compatibility API wrappers, so old applications
just continue to work as they always did.

However, I'm not adding compatibility wrappers for the plug-in API
that can be used to register additional authentication implementations
at runtime.  We don't know of any user of that API outside of
libosmocore, so the function signatures of the 'struct osmo_auth_impl'
are modified in an incompatible way.

Change-Id: Ie775fedba4a3fa12314c0f7c8a369662ef6a40df
2023-06-02 08:29:55 +00:00
Andreas Eversberg 5c7336ce88 ASCI: Add IE transcoding according to 3GPP TS 48.008
Change-Id: Ic1fc714bb04228a7f32e9925811e21c8efc610bd
2023-06-02 08:28:49 +00:00
Andreas Eversberg 19b0bb7dd2 ASCI: Add 3GPP TS 44.068 and 44.069 protocol definitions
Change-Id: I3554cea47e714c8fca18c3e9c0e6e80695915a90
2023-06-02 08:28:49 +00:00
Mychaela N. Falconia 6e529a2c74 coding: gsm0503_tch_{fr,hr}_encode(): add ability to emit BFI
Every BTS needs to have some graceful handling for the scenario
where it is time to send out a speech frame on TCH DL, but there is
no frame to be sent.  One possible solution is to transmit dummy
FACCH, but this option is unattractive for TCH/HS where FACCH
displaces two speech frames rather than one.  A more elegant
solution is to emit a speech frame with inverted CRC3, causing
the MS receiver to declare a BFI condition to its Rx DTX handler.
Setting all u(k) bits to 0 is one way to produce such an inverted-CRC
speech frame (normal TCH FR/HR CRC3 for an all-zeros frame would be
111), and this method is in fact what sysmoBTS PHY is observed to do.
Add the same ability to gsm0503_tch_{fr,hr}_encode() functions,
indicated by payload length of 0.

Change-Id: Iade3310e16b906efb6892d28f474a0d15204e861
2023-05-27 11:06:57 +00:00
Mychaela N. Falconia 18e5af55be codec: new function osmo_hr_sid_reset()
If a network element that receives call leg A UL and is responsible
for preparing leg B DL receives a GSM-HR SID frame whose SID field
is not all 1s but which is marked as valid SID by out-of-band means
(TRAU-UL frame control bits or the FT field in RFC 5993 ToC octet),
this SID frame should be rejuvenated (SID field reset to all 1s)
prior to retransmission on call leg B DL.  Provide a function
that performs this operation.

Related: OS#6036
Change-Id: Iebc0863ffcc3f8f25aeb54d4b14fac0487bc2bbb
2023-05-27 10:51:17 +00:00
Mychaela N. Falconia 2974a23c75 codec: new functions osmo_{fr,efr}_sid_reset()
In Iec5c1f2619a82499f61cb3e5a7cd03ff0f020ad8 we added
osmo_{fr,efr}_sid_preen() functions that apply SID classification
of GSM 06.31/06.81 section 6.1.1 (osmo_{fr,efr}_sid_classify()),
reject invalid SID, and "rejuvenate" deemed-valid SID frames by
resetting their SID code word, correcting the one bit error that
may be present in a deemed-valid SID frame.  However, the last
operation (rejuvenation of a SID frame by resetting its SID field)
should also be made available as its own function, as it may be
more efficient in some applications: for example, an application
may need to call osmo_{fr,efr}_sid_classify(), apply the rejuvenation
to valid SID, but also use the classification result to drive other
logic.  Factor out these functions.

Change-Id: I1d6dd867a358bdda8850cd8c959d0f361c0a5b6d
2023-05-27 10:49:41 +00:00
Mychaela N. Falconia 945f6cec0f coding cosmetic: gsm0503_tch_{fr,hr}_encode(): remove extra spacing
These two functions are structured as switch statements handling
different types of input, but they also have a lot of empty spacing
lines in strange places, making them difficult to read and even more
difficult to add new functionality while maintaining the same style.
Remove those extra spaces.

Change-Id: I595a80898283d0932fb33f582347ec39a9481d1a
2023-05-27 00:41:33 +00:00
Vadim Yanitskiy b04d59e997 coding: fix _tch_csd_burst_map(): do not overwrite FACCH
As was demonstrated in the unit test [1], FACCH bitstealing does not
work as expected in conjunction CSD specific encoding functions.

The problem is in _tch_csd_burst_map(): we don't check the stealing
flags hu(B) and hl(B) and overwrite both odd and even numbered bits
unconditionally.  Even worse, we reset these stealing flags to 0.

* Do not overwrite the hu(B) and hl(B) flags.
* Copy *even* numbered bits only if hu(B) is not set.
* Copy *odd* numbered bits only if hl(B) is not set.

Change-Id: Ib5395c70e3e725469c18ff7d4c47c62ddfdbd55d
Related: [1] Idc6decec3b84981d2aab4e27caab9ad65180f945
Related: OS#1572
2023-05-26 02:48:21 +07:00
Vadim Yanitskiy 31f761f7c1 coding: test FACCH/[FH] bitstealing in test_csd()
In test_csd() we encode three data frames filled-in with three specific
patterns and then try decoding them.  Additionally execute the same set
of tests, but with FACCH/[FH] bitstealing (pattern 0x2b).

As can be seen from the test output, we have problems decoding FACCH:

* FACCH/F: decoding fails (n_errors=0 / n_bits_total=0),
* FACCH/H: decoding with errors (n_errors=2 / n_bits_total=456).

A patch fixing the problem follows.

Change-Id: Idc6decec3b84981d2aab4e27caab9ad65180f945
Related: OS#1572
2023-05-26 02:48:18 +07:00
Vadim Yanitskiy b334022aba coding: implement dedicated codec API for FACCH/[FH]
Currently FACCH/[FH] encoding and decoding is implemented as part of
the gsm0503_tch_[fh]r_{en,de}code and gsm0503_tch_a[fh]s_{en,de}code
API.  This works fine for speech because one FACCH frame completely
replaces one or two speech frames, but this is not the case for CSD.

According to 3GPP TS 45.002, sections 4.2.5 and 4.3.5, for TCH data
channels FACCH does not replace data frames but disturbs some amount
of bits from them.  Therefore we need to be able to perform FACCH
encoding and decoding independently from CSD specific coding API.

Change-Id: I0c7a9c180dcafe64e6aebe53518d3d11e1f29886
Related: OS#1572
2023-05-26 02:35:11 +07:00
Vadim Yanitskiy 9a22827cf6 coding: implement TCH/F9.6, TCH/[FH]4.8, TCH/H2.4, TCH/F14.4
Implement all CSD specific channel modes, except TCH/F2.4.  All of
these modes are more or less similar to each other.  The TCH/F2.4
is more similar to TCH/FS and slightly more complicated.

FACCH/F and FACCH/H will be implemented in a follow-up change.

Change-Id: Ib482817b5f6a4e3c7299f6e0b3841143b60fc93d
Related: OS#1572
2023-05-25 19:08:53 +07:00
Vadim Yanitskiy ce484eb035 gsm: fix convolutional code definition for TCH/F4.8
The block length for TCH/F4.8 is off by 4 bits.  Value 152 matches
with what TS 45.003 section 3.4.3 defines, but for some reason the
encoder generates more bits (468) than it must (456).  This results
in buffer overruns when encoding TCH/F4.8.

All block length values in conv_codes_gsm.py are 4 bits less than
the respective values in TS 45.003.  I have no idea why...

Change-Id: Id86d1aa0fd6791a8be431b5547bb723c74c35757
Related: OS#1572
2023-05-25 19:08:53 +07:00
Vadim Yanitskiy e677fccf44 coding: use gsm0503_tch_hr_decode2() in coding_test
Let's make sure the new API is covered by unit tests.
This patch also fixes a deprecation warning.

Change-Id: I4dea43f68941b5986ecc51b2e41c80741a711002
Related: 57a3b3a51f
2023-05-25 14:58:21 +07:00
Vadim Yanitskiy 8fb56473aa coding: fix API doc: TCH/AFS vs TCH/AHS
Change-Id: Iaed1653c6001545f69ec6b466760f30d42bc4386
2023-05-23 19:16:39 +00:00
Vadim Yanitskiy 98431bb3f4 coding: fix API doc: TCH/H needs 6 bursts, not 8
Change-Id: Iea787ec4c3b15a74f59d45825eae03740c5d28fa
2023-05-23 19:16:39 +00:00
Mychaela N. Falconia 0e17d57495 libosmocoding.map: export gsm0503_tch_hr_decode2()
In commit 57a3b3a51f I added new function gsm0503_tch_hr_decode2(),
but I forgot to add it to libosmocoding.map - and without the latter,
osmo-bts-trx and other applications can't use the new function.
Fix that omission.

Related: OS#5688
Change-Id: I6e75ca95409b5c368e8e04d0e0aba41e0331d9e6
2023-05-23 19:02:53 +00:00
Mychaela N. Falconia 57a3b3a51f gsm0503_tch_hr_decode2(): new function, emits TS101318 format
The original design of gsm0503_tch_hr_{en,de}code() functions contains
a mistake in that a pseudo-RFC5993 format was chosen for HR codec frame
input and output, instead of "pure" (agnostic to outer RTP encoding)
form of 14 bytes.  We would like to change this design so that we can
feed pure 14-byte HR codec frames to the channel coding function and
get such frames back from the channel decoding function - however,
we cannot break libosmocoding API for existing users.  In the decoding
direction, create a new function that emits TS 101 318 format, and
turn the legacy gsm0503_tch_hr_decode() API into a wrapper function
for backward compatibility.

Related: OS#5688
Change-Id: If28ddb20789e8993b7558ca08020478615b4c708
2023-05-23 16:42:01 +00:00
Mychaela N. Falconia 8d8d5af957 gsm0503_tch_hr_encode(): accept both TS101318 and RFC5993 payloads
The original design of gsm0503_tch_hr_{en,de}code() functions contains
a mistake in that a pseudo-RFC5993 format was chosen for HR codec frame
input and output, instead of "pure" (agnostic to outer RTP encoding)
form of 14 bytes.  We would like to change this design so that we can
feed pure 14-byte HR codec frames to the channel coding function and
get such frames back from the channel decoding function - however,
we cannot break libosmocoding API for existing users.  In the encoding
direction, make the new format our preferred one, but support the
extra-octet format for backward compatibility.

Related: OS#5688
Change-Id: I13eaad366f9f68615b9e9e4a5f87396a0e9dea0f
2023-05-23 16:41:05 +00:00
Mychaela N. Falconia 920c0ed86f gsm0503_tch_hr_decode(): look at all 8 stealing bits
This function needs to look at hl and hu stealing bits in order to
decide if it should decode FACCH/H instead of TCH/HS traffic.
However, out of the 8 (in total) hl and hu bits that get set when
FACCH/H stealing takes place, the function only looked at 7 of them
- one was missed.  Fix this bug.

Change-Id: I08c4358b26d69910190f89a53b654bc58c2efea9
2023-05-23 14:06:53 +00:00
Neels Hofmeyr a9c29b45ea gsm_04_08_gprs: add IEI "GMM TMSI Based NRI Container"
OsmoHNBGW will need to obtain the NRI from GMM Attach Request and GMM
RAU Request to implement CN pooling.

Related: SYS#6412
Change-Id: Id661abfdb2c81a92c9046542bbc08d6ccd39f073
2023-05-22 15:35:02 +00:00
Harald Welte 1dffb53aac gsm_08_58.h: Add 'struct rsl_ie_nch_drx_info'
This adds the definition of 'struct rsl_ie_nch_drx_info' representing
the bit-field of the 'NCH DRX Information IE' of A-bis RSL.

Change-Id: I9586b5cb8514010d9358fcfc97c3d34741294522
Related: OS#5781
2023-05-22 10:33:10 +00:00
Harald Welte 8079a0636e cosmetic: Fix spec reference in RSL header file
Change-Id: I671ee927b49099f7c8cc1fbd5f8b19f94ba1af81
2023-05-22 10:33:10 +00:00
Harald Welte 1192687620 Add osmo_gsm48_si1ro_nch_pos_{encode,decode} functions
These functions encode/decode the NCH position field within the SI1
rest octets.  This is used within ASCI (VBS/VGCS).

Change-Id: I24a0095ac6eee0197f9d9ef9895c7795df6cdc49
Related: OS#5781
2023-05-22 10:33:10 +00:00
Vadim Yanitskiy 242ef1bdea coding: declare gsm0503_tch_f96_[de]interleave()
Change-Id: I9a631db088a4e279668beb962c98cc1a0929f44d
Related: OS#1572
2023-05-20 19:06:11 +07:00
Vadim Yanitskiy 548990bf6c coding: gsm0503_tch_f96_[de]interleave() not applicable to TCH/F2.4
According to 3GPP TS 45.003, section 3.6.4, the interleaving for
TCH/F2.4 is done as specified for the TCH/FS in subclause 3.1.3.

Change-Id: I52078263cd593503a9e8f024e51e18d7b0906131
Related: OS#1572
2023-05-20 19:06:11 +07:00
Vadim Yanitskiy c0305b685a coding: use GSM_MACBLOCK_LEN gsm0503_tch_fr_decode()
Change-Id: I488ca3db1ca093d19abcfba78c1516509870715d
Related: OS#1572
2023-05-20 19:05:41 +07:00
Daniel Willmann cbbd17e3f4 osmo_io: Support detecting non-blocking connect()
libosmo-netif does a non blocking connect(), which as per definition of
 the socket API is signalled from the OS to the user by marking the file
descriptor writable.

osmo_io needs to signal this somehow. Previously osmo_io would only call
the write_cb if actual data has been sent. This patch changes the behaviour
so that calling osmo_iofd_write_enable() will call write_cb() on a writable
socket even if the write queue is empty.

Change-Id: I893cbc3becd5e125f2f06b3654578aed0aacadf3
2023-05-19 12:50:21 +00:00
Daniel Willmann d4d03705f9 osmo_io: Improve handling and documentation of segmentation_cb
The read length is not needed in the segmentation callback, msgb
already has all the necessary information, the parameter previously was
just msgb_length(msg).

Also handle negative return values (except -EAGAIN) of the callback as
errors which cause the msg to be dropped. -EAGAIN will defer the msg.

Change-Id: I6a0eebb8d4490f09a3cc6eb97d4ff47b4a8fd377
2023-05-19 12:50:21 +00:00
arehbein 0c374c68a1 core: Add function to update osmo_io_ops field for osmo_io_fd
Added, because the field 'io_ops' of 'struct osmo_io_fd' is not a
reference, so subsequent changes to the osmo_io_ops structure that was
used to set it up aren't automatically reflected in the osmo_io_fd
structure that got its copy.

Change-Id: Ie45402ad8e86e3cecf75ad78a512c17e61e68b19
2023-05-19 12:50:24 +02:00
Vadim Yanitskiy 03590fcee4 copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
Change-Id: Ia0952b3b0ce6871e92fa02a3c6ea69c211a0c04d
2023-05-19 08:48:48 +00:00
Mychaela N. Falconia f3e4745603 coding: fix decoding of EFR triplicated bits
In GSM 05.03 channel coding for EFR, there are 4 bits out of the
244-bit EFR codec frame that are triplicated before FR-like encoding,
and the decoder needs to apply a 2-out-of-3 majority voting function
to each of these triplicated bits after the FR-like decoding step.
For one of those 4 bits, this majority voting function was wrong
in the decoder implemented in gsm0503_tch_fr_decode() - fix it.

Change-Id: I47def75cdb066ed6372df4567404cc828589ed53
2023-05-19 08:44:35 +00:00
Pau Espin d9d61b0188 cosmetic: codec/Makefile.am: list sources one file per line
Change-Id: I299fdb887f640151bf57d6423b0e08ffa8cd02f8
2023-05-18 10:54:26 +02:00
Mychaela N. Falconia 5eb356be99 codec: replace GSM-FR ECU with new implementation
The original GSM-FR ECU implementation from 2017 exhibits a lot of
defects, as detailed in OS#6027.  Replace it with a new implementation
based on Themyscira libgsmfrp (a complete Rx DTX handler for GSM-FR),
but reduced to just the ECU function, without the comfort noise
generator function.  (These two functions are coupled together in the
classic GSM architecture, but not in libosmocodec ECU model.)

Related: OS#6027
Change-Id: I0200e423ca6165c1313ec9a4effc3f3047f5f032
2023-05-17 13:53:20 +00:00
Mychaela N. Falconia 8fa13c66ac codec cosmetic: move old FR ECU code to ecu_fr_old.c
The current FR codec ECU implementation consists of two parts:
there is a pair of functions that implement the actual functionality,
and these functions are also made public as a now-deprecated legacy
API - and there is the new ECU abstraction.  If the FR ECU
implementation behind the generic ECU abstraction is to be changed
to a different one, the old implementation still has to be retained
for those public legacy osmo_ecu_fr_reset() and osmo_ecu_fr_conceal()
API functions to remain available and unbroken.  Move this legacy
ECU implementation to its own C module in preparation for changing
the preferred ECU implementation.

Related: OS#6027
Change-Id: Ia169b8bcc6331227a11b78eb7ffca0c7ab838c69
2023-05-17 13:53:20 +00:00
Pau Espin 8f026bf3b7 ns2: Count transmitted/dropped in each layer implementation
This allows better control on how the counters are ticked.
For instance, since nowadays the writing in ns2_udp is done
asyncrhonously, most probable failures occur at a later point and not
when returning to the caller.

Change-Id: I8109cee07f157ebf1806f82a071f58de3a2dcc9c
2023-05-16 08:04:40 +00:00
Daniel Willmann fa3a9ce9fd gpsr_ns2_udp: Use osmo_io_fd instead of osmo_fd
Change-Id: Id776d2d9f35c304620f3d5b94492148dd987f5a0
2023-05-16 08:04:23 +00:00
Vadim Yanitskiy fed02e01d9 coding: fix doxygen doc for _xcch_encode_cB()
Change-Id: I09ed28bd3060b9c2c958f0f465a92af7d6bacadd
2023-05-15 16:50:08 +00:00
Daniel Willmann a4b958bda2 osmo_io: Avoid read of uninitialized variable
Change-Id: Ica8087c73fc10f01026fdbe692b172ad07fe593d
Fixes: CID#315618
2023-05-15 18:10:25 +02:00
Mychaela N. Falconia f86ec508e9 codec: add osmo_gsm611_silence_frame[] datum
Table 1 in section 6 of 3GPP TS 46.011 (FR codec, substitution and
muting of lost frames) specifies a silence frame in the form of
GSM 06.10 parameters - add a const datum to libosmocodec embodying
this GSM 06.11 silence frame in GSM-FR RTP encoding.

Related: OS#6027
Change-Id: Idf31051ea783435268944286a71d2b0ac342a4b5
2023-05-10 18:18:42 +00:00
Daniel Willmann 37b2ebfc84 tests: Add initial osmo_io tests
Change-Id: Ia67629e53f4d2e5784177250d58e268fdfcaa0c2
2023-05-10 06:06:03 +00:00
Harald Welte 8857f3b798 Add osmo_io with initial poll backend
* make backend configurable for later
* segmentation callback for chunked streams
* logging target for osmo_io
* support partial writes

Change-Id: I50d73cf550d6ce8154bf827bf47408131cf5b0a0
Related: SYS#5094, OS#5751
2023-05-10 06:04:57 +00:00
arehbein 99653047d7 core: Check return value of osmo_fd_register()
Change-Id: I15bb456c42d5033c9cf4c0937df22fa02b6a6086
2023-05-09 23:37:34 +00:00
Mychaela N. Falconia 341c4ec3a4 codec: add osmo_{fr,efr}_is_any_sid() inline functions
Recently added osmo_{fr,efr}_sid_classify() functions classify FR
(EFR) codec frames according to the rules of GSM 06.31 (06.81)
section 6.1.1.  Both of these specs also define the term "accepted
SID frame", encompassing both valid and invalid SID frames, but not
regular speech frames.  A boolean check for this wider category of
"accepted SID frame" is a useful function - many existing calls to
legacy osmo_{fr,efr}_check_sid() functions should be converted
to this "accepted SID frame" check for full correctness.  Add wrapper
functions for convenience, and to allow this transition to be made
without adding bloat to every use instance.

Change-Id: I5e6e91baf18440af01dccc6ac0171476a8a5c71c
2023-05-09 07:51:12 +00:00
Neels Hofmeyr e25786ab6a gsm: add osmo_mobile_identity_decode_from_l3_buf()
We have osmo_mobile_identity_decode_from_l3(), which takes a msgb as
argument, and decodes msg->l3h. Not all callers have their data in this
form. Offer a more flexible API for the same decoding.

For example, before the new function, osmo-hnbgw, which extracts a NAS
PDU from asn.1 packed data for CN pooling, would allocate a new msgb and
copy the NAS data just to pass a data pointer as argument.

Related: SYS#6412
Change-Id: I9bd99ccd01f0eedc091fe51687ff92ae1fdff60b
2023-05-06 03:49:08 +00:00
Neels Hofmeyr e4b84738b5 vty: move struct vty_parent_node to private API
Change-Id: Id2070f03b09feea966c5342361d409551e557d38
2023-05-06 03:49:08 +00:00
Neels Hofmeyr 67d84d2131 vty: fix vty->index for implicit go_parent_node
After this patch, most vty_go_parent() functions are really obsolete, as
originally intended: A vty_go_parent() is only needed if the program
requires an action to run on VTY node exit.

vty_transcript_test.vty shows the fixed behavior.

For details, see preceding patch
"vty: show bug in implicit go_parent_node"
I2472daed7436a1947655b06d34eb217e595bc7f3

Change-Id: Id408c678d18ba19b1c1394c3fb657536153d2094
2023-05-06 03:49:08 +00:00
Neels Hofmeyr cc9b699931 vty: show bug in implicit go_parent_node
Add test to show a problem in VTY node exiting.

Back in 2017 when I introduced VTY config file scopes by indenting [1],
I actually mistook the vty->priv for the vty->index that we use
everywhere to link to the state for our VTY nodes.

The intention was that each VTY node child level has its own object
linked to it by the vty->index pointer. When the config file leaves a
scope, the vty->index should reflect the parent object.

Instead I implemented that for the vty->priv pointer only, but we don't
use that.

Why did this bug not show? A problem happens only if:
- a node that uses vty->index is nested inside a node that also uses
  vty->index.
- config sets parent node attributes after a child node.
- there is no legacy vty_go_parent() function that sets the correct
  index via a switch().

[1]
"VTY: implicit node exit by de-indenting, not parent lookup"
4a31ffa2f0
I24cbb3f6de111f2d31110c3c484c066f1153aac9

Change-Id: I2472daed7436a1947655b06d34eb217e595bc7f3
2023-05-06 03:49:08 +00:00
Philipp Maier 4cf72df3f9 codec: add define constants for RFC5993 and TS101318
The difference between the RFC5993 and the TS101318 format is only that
RFC5993 has one additional ToC (Table of contents) byte at the
beginning. However it can be difficult to remember which of the two
formats has the ToC byte at at the beginning and which hasn't.

Let's add a constant that defines the length for both formats so that we
can make it more clear in the code which format we are refering to.

Related: OS#5688
Change-Id: I125ef9cdab98c073971841c175b1a7dcd927f9c2
2023-05-04 00:10:47 +00:00