3GPP-Session-Stop-Indicator

Illustration

Fair amount of my time is spent in 3GPP specification world. Quite often I do that to figure out some minor details of random AVP I am analyzing. And no, I won’t probably go to the specification directly if search can work as well. So, imagine my surprise when I found 3GPP-Session-Stop-Indicator is specified incorrectly wherever I looked for it.

Want examples? Check it here, or here, or even here. Every time you see it specified, you will find the same data type: UTF8String. Which sounds plausible until you see it in the real world diameter snoop. There you’ll find its content is always 0xFF. So, what’s wrong with that?

If you read through RFC 6733, you will see that UTF8String data type is defined to use UTF-8. Guess what bytes sequence can NEVER appear in UTF-8? Darn 0xFF. As per RFC 3629, the highest byte value actually defined would be 0xF3. If you know anything about Diameter, the only sane answer is that this AVP is supposed to be of OctetString data type. So, where is this confusion coming?

I would place blame on ETSI TS 129.061 aka 3GPP 29.061. Incidentally, you won’t find 3GPP-Session-Stop-Indicator text anywhere in the specification. But you will find 3GPP-Session Stop Indicator defined with the following text:

3GPP Type: 11
Length: 3
Value is set to all 1.
3GPP-Session Stop Indicator value: Bit String type

Both 3GPP-Session Stop Indicator and 3GPP-Session-Stop-Indicator share code 11 with vendor 10415 (yes, we’ll ignore 3GPP calling it a “type” instead of “code”). They are the same AVP. Interestingly, length is specified as 3 which makes no sense as far as diameter goes. This is because this wasn’t originally specified for Diameter but for RADIUS. And, in RADIUS, length is indeed 3 bytes (or octets if you want to be fancy) once we add header to the single data byte. When Diameter came, 3GPP just promoted it into the new world without bothering to redefine it in any follow-up specification.

Now that we know we’re looking at the correct AVP, despite slight differences in name and length, what is the definition? It is defined as a bit string with all bits set to 1. Not UTF-8. Bit string. Which, in Diameter world, would mean its type is OctetString. And something that’s a single byte with all ones set is also known as 0xFF.

My guess is that someone originally misinterpreted “bit string” for “string” and thus it got written as UTF8String in the dictionary. Since 0xFF is just a single byte, nobody actually bothered to set it as string but they assign direct value when it comes from the sender. On consumer side, why would you even read this AVP when its value is always the same - there is no other value that 0xFF defined in any of 3FPP specifications as far as I can see. Thus, received only checks if AVP is present. Maybe some also check content but don’t bother converting to UTF-8 for a single byte. And that’s it - mystery solved - this was an OctetString all along.

Am I the first person to find this issue? I doubt it - anybody converting this to UTF-8 would see the issue. It is clearly visible in the darn Wireshark as text converts to “�” which is what UTF-8 decoder would return when it CANNOT convert. But I guess that most people looking at this were smart enough to just let it go. :)

Larger Live File System on Fedora 44

Illustration

While I am spending most of my time on Ubuntu these days, I do like to see what Fedora is up to. So, with Fedora 44 out, I started medling with installing some extra ZFS packages on top of it. However, I was soon hit with “Npt enough space” message. Fedora’s in-memory root pertition was a bit too small. But there’s a way around it.

During boot just select the main boot option and press e key. This will lead you into text editor and there just add

rd.live.overlay.size=10G

to the “linux” line.

This will make in-memory store larger (albeit you won’t see it be 10G in size). With this, I was finally able to fit ZFS on it.

Dealing with "No Signal" on HDMI Input

Illustration

For my media PC I use old Intel’s gen 11 board. As I upgraded my Framework laptop, it just made sense to use it. Bazzite works flawlessly on it. At least until paired with my Samsung “smart” TV.

I might be turning into an old man yelling at clouds but it seems that smart TVs are getting dumber and dumber. My PC is connected to TV via HDMI cable and it works wonderfully. Until TV is off for some time. Then suddenly, TV cannot find HDMI source.

I already wrote about this so I will spare you details on how I figured the TV was to blame. Suffice it to say that I found the fix even back then. But now I had an issue with my fix. It was too slow.

You see, I decided to use 1 minute timer. Which means that, once TV has gone crazy, one had to wait a minute at worst. And that caused some impatience in my household. So, I needed a faster detection…

The new script at /usr/local/bin/dp-reconnect looks something like this:

#!/usr/bin/env bash

while(true); do
    SLEEP_INTERVAL=$(( 3 - `date +%S` % 3 ))
    sleep $SLEEP_INTERVAL

    DP_CONNECTED=0
    for DP_PATH in /sys/class/drm/card1-DP-*; do
        DP_STATUS=$( cat "$DP_PATH/status" | grep '^connected$' | xargs )
        if [[ "$DP_STATUS" == "connected" ]]; then
            DP_CONNECTED=1
            break
        fi
    done

    if [[ $DP_CONNECTED -ne 0 ]]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') Display $( basename "$DP_PATH" ) connected"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') No connected displays"
        chvt 2
        sleep 1
        chvt 1
    fi
done

Instead of “one-shot”, this script loops forever. The very first calculation is there just to slow things a bit and ensure payload is executed every 3 seconds. And yes, this could have been replaced with sleep 3 without any functionality loss. However, I like to “align” my execution times so I did it in slightly more complicated manner.

To run this code, we can simply create /etc/systemd/system/dp-reconnect.service with the following content:

[Unit]
Description=Switch terminal if no DP is connected

[Service]
ExecStart=/usr/local/bin/dp-reconnect
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

With all files in place, the only remaining thing was enabling the service:

sudo systemctl daemon-reload
sudo systemctl enable --now dp-reconnect.service

Now my TV reacts much faster, hopefully putting this story to an end.

Chrony Tracking in Milliseconds

If you are running NTP server based on Chrony, you are probably checking its status too. For me chronyc tracking has been almost perfect. The only thing I dislike is having seconds in outputs. For any decent NTP server, these values are sub-millisecond and thus output has a lot of zeros.

Fortunatelly, changing seconds to milliseconds is easy:

chronyc tracking | awk '
  /seconds/ {
    sub(/seconds/, "milliseconds")
    for (I=1; I<=NF; I++) {
      if ($I ~ /^[+-]?[0-9.]+$/) {
        $I = sprintf("%.6f", $I * 1000)
      }
    }
  } { print }'

This awk command will match any line that has word seconds with milliseconds and then multiply any number it finds with 1000. Once printed again, this means all numbers are in milliseconds, just as we wanted.

This does work but it will also adjust Update interval, which is one field that makes sense to be in seconds. So, we can limit how many changes are to be done.

chronyc tracking | awk '
  /seconds/ && count < 5 {
    sub(/seconds/, "milliseconds")
    for (I=1; I<=NF; I++) {
      if ($I ~ /^[+-]?[0-9.]+$/) {
        $I = sprintf("%.6f", $I * 1000)
      }
    }
    count++
  } { print }'

Here we just add counter to limit our changes to only the first 5 fields. Since chronyc tracking output is relatively stable, we got to “cheat” a bit.

If you’re using this as input for something else, this is sufficient. However, if you plan to look at it on the command line, you will notice that changed lines also had their spaces messed up. For that, here is the final form.

chronyc tracking | awk '
  /seconds/ && count < 5 {
    sub(/seconds/, "milliseconds")
    for (I=1; I<=NF; I++) {
      if ($I ~ /^[+-]?[0-9.]+$/) {
        $I = sprintf("%.6f", $I * 1000)
      }
    }
    count++
  } { print }' | awk '{
    sub(/:/, "\t")
    print
  }' | column -t -s$'\t' -o:

Here we make use of the column command to make a nice table using tab (\t) as an input and colon (:) as output separator. Reason we cannot use colons for input separation lies in ref time field which includes colons in the time string. So, in order for this to work, we also adjust input to column by replacing the first colon with tab and leaving the rest intact.

Now your output should look something like this:

Reference ID    : 81060F1C (time-a-g.nist.gov)
Stratum         : 2
Ref time (UTC)  : Sun May 24 00:00:01 2026
System time     : 0.207166 milliseconds fast of NTP time
Last offset     : 0.010373 milliseconds
RMS offset      : 0.052737 milliseconds
Frequency       : 23.102 ppm slow
Residual freq   : +0.000 ppm
Skew            : 0.006 ppm
Root delay      : 24.370797 milliseconds
Root dispersion : 1.106555 milliseconds
Update interval : 1033.8 seconds
Leap status     : Normal

PTClipboard

As I was switching to Linux, so did my code base. I admit, most of code didn’t really care on which platform it was running but I wasn’t so fortunate with all of it. One annoying issue was clipboard handling. Linux simply did its clipboard differently. So I decided to solve this problem for me. Twice.

My first attempt at solving this problem was a simple X11 clipboard library. It worked like a charm. And then the whole migration toward Wayland happened. While most desktop environments still have X11 compatibility, writing was on the wall for this one.

So, I decided to expand functionality a bit. The new library still supports handling X11 clipboard text. And to it adds both Wayland and Windows support. That way the same library can be used in a really cross-platform manner. Well, almost cross-platform - MacOS support is missing as I don’t really have any devices to test it on.

Now, this library is small in scope - only plain-text is supported. While I do have plans to adjust this a bit, I doubt it will go much beyond this. Idea is to have it as simple as possible, not to provide the full clipboard access. And simple it is:

using Medo;

PTClipboard.SetText("My text.");
var text = PTClipboard.GetText();

And that is pretty much all. Yes, there is also support for primary selection under linux (aka as “middle-click clipboard”) with syntax just slightly longer:

using Medo;

PTClipboard.Selection.SetText("My text.");
var text = PTClipboard.Selection.GetText();

And setup is pretty much non-existent as code should detect system it is running on automatically.

You can get package at NuGet and check source code at GitHub.