2026/09/16

Setting up firewall rules for a LG TV

 

I have an LG TV that, by my own choice, does not have Internet access.

It is not disconnected from my network. It is connected to the LAN, uses my DNS server, and can communicate with whatever I decide to allow. I simply have a firewall rule preventing it from accessing the Internet.

My home infrastructure uses shadowcat4 as the router/firewall, a fanless mini PC with an Intel N95 and four Intel I226-V 2.5 Gb Ethernet interfaces. It runs Slackware-current and replaced my old shadowcat3 router.

The Internet connection comes through FTTH with the ONT in bridge mode. Shadowcat4 establishes the PPPoE connection directly and also runs nftables, OpenVPN, dnsmasq, dnscrypt-proxy, and a few other things.

The LAN is 192.168.111.0/24, shadowcat4 is 192.168.111.14, and the LG TV is 192.168.111.191.

The problem appeared because of a rather silly characteristic of the TV: if it completely loses electrical power, it loses its date and time. When power comes back, it may boot believing that it is sometime in 2022.

That by itself would not be a major problem, except that I use the TV's automatic power-on feature. I want it to turn itself on at 06:00, and obviously it needs to know what time it is for that to work.

With Internet access enabled, it recovered the correct time automatically. With Internet access blocked, it did not.

So the question became quite specific:

What is the absolute minimum Internet access the TV needs solely to recover its date and time?

First suspect: NTP

The first thing I thought of, and the first thing I discussed with ChatGPT during the troubleshooting process, was the obvious candidate: NTP.

If a device needs to know the time, one would expect UDP traffic to port 123.

Shadowcat4 already uses NTP and, in fact, during the tests another interesting problem showed up: NTP queries leaving through my PPPoE connection were receiving replies very inconsistently. We captured traffic on ppp0, verified the outgoing queries, and compared the results with another machine using a different ISP.

From that other connection, the same NTP servers responded quickly and consistently.

Everything pointed toward some behavior involving the ISP or the network path used by shadowcat4.

Interesting, but ultimately a false lead for the TV problem.

Capturing the LG's traffic after a cold boot revealed something much more important:

the TV was not attempting to use UDP/123 at all.

No NTP.

Instead, it was repeatedly trying to establish TCP connections to port 443.

Opening HTTPS as a test

The firewall uses nftables with a DROP policy. The LG was given a specific rule before the general LAN access rules:

ip saddr 192.168.111.191 oifname "ppp*" drop

As a test, we temporarily inserted an exception before that DROP rule allowing the TV to connect to any destination over TCP/443.

Then I performed a real cold boot: unplugged the TV from power, waited, and plugged it back in.

It started without the correct time.

With HTTPS allowed, it recovered the correct date and time.

That completely changed the direction of the investigation. We were no longer looking for an NTP server. Some LG service accessible over HTTPS was involved in recovering the time.

Using DNS as a guide

The TV uses dnsmasq on shadowcat4 as its DNS server, so I started logging its queries.

Several domains appeared:

lgtvonline.lge.com
BR7.nextlgsdp.com
AIC.tv.wiselg.com
aic-gfts.nextlgsdp.com
qt2-kic.lgtviot.com
aic.lgtviot.com
BR7.info.lgsmartad.com

The last one was particularly amusing because my HaGeZi blocklist causes:

BR7.info.lgsmartad.com

to return NXDOMAIN.

The TV recovered its time perfectly anyway.

So advertising was out of the picture.

The domain that started looking particularly interesting was:

BR7.nextlgsdp.com

dnsmasq was returning a set of addresses such as:

54.200.71.5
54.186.200.227
32.189.217.166
16.146.234.1
32.188.96.203
52.41.138.142
52.35.116.50
16.147.145.11

The set is not permanent: the addresses rotate and change over time.

Capturing the exact moment

For most of the testing I used tcpdump.

I recorded a complete capture:

tcpdump -ni eth0 -s0 -w /tmp/tv-br7.pcap host 192.168.111.191

Before opening the firewall, there was a succession of SYN packets from the TV toward HTTPS servers, but no replies because nftables was dropping them.

After enabling the exception, the first SYN/ACK appeared:

13:47:57.785790
52.35.116.50:443 -> 192.168.111.191
Flags [S.]

A few seconds later, connections started appearing from:

52.41.138.142:443

Both addresses were part of the set DNS had just returned for BR7.nextlgsdp.com.

The correlation was very strong, but it was still a correlation.

I wanted to prove that those HTTPS connections were actually intended for that hostname.

Enter tshark

Until this point, I did not have tshark installed on shadowcat4.

I installed Wireshark 4.6.8 using the SlackBuild available from SlackBuilds.org. I did not really need the Wireshark graphical interface; I only wanted tshark to analyze the pcap file I had already generated with tcpdump.

The useful part is that, although HTTPS encrypts the application data, the TLS ClientHello can contain the SNI identifying the server name the client wants to reach.

I ran:

tshark -r /tmp/tv-br7.pcap \
  -Y 'ip.src==192.168.111.191 && tls.handshake.extensions_server_name' \
  -T fields \
  -e frame.time \
  -e ip.dst \
  -e tls.handshake.extensions_server_name

And there it was:

2026-09-16T13:47:57.800548000-0300  52.35.116.50   br7.nextlgsdp.com
2026-09-16T13:48:02.134213000-0300  52.41.138.142  br7.nextlgsdp.com
2026-09-16T13:48:05.790571000-0300  52.41.138.142  br7.nextlgsdp.com
...

Case closed.

We no longer merely knew that those IP addresses had been returned by DNS for BR7.nextlgsdp.com: we now had the TV's own TLS ClientHello declaring through SNI that it wanted to talk to:

br7.nextlgsdp.com

The definitive test

We then performed the test that actually mattered.

The firewall was configured to block all Internet access from the TV except TCP/443 toward the eight addresses that BR7.nextlgsdp.com resolved to at that moment.

Then I performed another real cold boot by physically removing power from the TV.

The LG recovered the correct date and time.

During that test it had no access to the other services it had queried through DNS. It also could not access the advertising service blocked by HaGeZi.

So, for my practical purposes, the result was demonstrated:

the TV can remain without general Internet access and only needs HTTPS access to BR7.nextlgsdp.com to recover its date and time.

I am not claiming that TLS itself is being used as a time synchronization protocol. TLS does not normally provide system time that way. Some response or part of the LG service available over HTTPS is apparently what allows the TV to obtain the information it needs.

For my firewall, that distinction does not matter. I now know which traffic needs to be allowed.

The resulting nftables configuration

The important part of the forward chain now conceptually looks like this:

TV -> current BR7.nextlgsdp.com IPs:443   ACCEPT
TV -> Internet                            DROP
established,related                       ACCEPT
LAN -> Internet                           ACCEPT

In /etc/nftables.conf I have:

ip saddr 192.168.111.191 \
ip daddr { 54.200.71.5, 54.186.200.227, 32.189.217.166, 16.146.234.1, \
           32.188.96.203, 52.41.138.142, 52.35.116.50, 16.147.145.11 } \
oifname "ppp*" tcp dport 443 accept

ip saddr 192.168.111.191 oifname "ppp*" drop

The ordering is deliberate.

The exception must come before the TV's DROP rule, and both must come before established,related and the general LAN-to-PPPoE rule.

That way, even an already-established connection from the TV cannot bypass its specific policy.

The dynamic IP problem

Naturally, one final detail remained: those eight addresses are not permanent.

My dnsmasq is version 2.93, as packaged by Slackware-current:

Dnsmasq version 2.93

Interestingly, it recognizes the --nftset option, but the official binary was compiled with:

ipset no-nftset

I could rebuild dnsmasq with nftset support and create an elegant integration between DNS and a named nftables set.

But by this point I was thinking something along the lines of: one more spot isn't going to matter to the tiger.

Updating eight IP addresses once a day does not need to become an engineering project.

So, with ChatGPT's help, I created:

/root/bin/update-lg-tv.sh

The script:

  1. queries the current A records for BR7.nextlgsdp.com using dig;

  2. extracts the IP addresses currently allowed for 192.168.111.191:443 from nftables;

  3. sorts both sets so DNS response ordering does not matter;

  4. exits without doing anything if they match;

  5. if they differ, updates the corresponding rule in /etc/nftables.conf;

  6. validates the new configuration with nft -c;

  7. saves a backup;

  8. loads the new ruleset;

  9. records what happened using logger.

There is also one important safety precaution: if dig returns no IPv4 addresses, the script changes nothing. A temporary DNS failure cannot cause a perfectly good whitelist to be replaced by an empty one.

Its first execution was exactly as boring as an administration script should be:

# /root/bin/update-lg-tv.sh
# echo $?
0

The DNS addresses matched the firewall addresses, so it did nothing.

The script can now simply be run once a day from cron. I do not need to chase DNS changes in real time: the only purpose of this exception is to let the TV recover its clock after completely losing electrical power.

As for organization, I prefer administration scripts to remain readable. If this logic grows, instead of accumulating embedded awk, sed, and subsidiary logic inside one large shell script, I will split it into small scripts under a directory such as:

/root/bin/lg-tv/
    update.sh
    get-dns-ips.sh
    get-nft-ips.sh
    update-nft-conf.sh

Not because Bash cannot do everything in one file, but because six months from now I want to be able to look at a script and immediately understand what it does.

What I liked about this experiment

The most interesting part of the whole exercise was the method.

We started with a perfectly reasonable hypothesis:

the TV needs NTP.

The evidence said otherwise.

Then we saw HTTPS.

We allowed only port 443 and verified that the TV recovered its clock.

We logged DNS queries to identify candidate services.

We reduced firewall access exclusively to the addresses belonging to one domain.

We repeated the cold boot.

It worked.

Finally, we used tshark on the packet capture to extract the TLS SNI and confirm that the connections actually getting through the firewall were going to br7.nextlgsdp.com.

The interaction with ChatGPT was particularly useful during this process, not because it could tell me in advance that "LG gets its time from this particular server," but precisely because it could not. We built hypotheses, designed tests to eliminate them, and looked at actual evidence coming from my own network.

Some paths led nowhere — the initial NTP investigation being the obvious example — but they were still useful because they eliminated possible explanations.

In the end, there was no need to trust LG documentation, Internet blocklists, or assumptions about what a Smart TV should be doing.

We watched its packets and proved what it was actually doing.

And shadowcat4 continues doing exactly what I wanted from the beginning:

the TV is connected to my network, but that does not mean it has permission to freely talk to the Internet.

2025/07/07

Disabling Annoying SIM Toolkit Pop-ups on Samsung Galaxy A01 Core (Claro Argentina)

Disabling Annoying SIM Toolkit Pop-ups on Samsung Galaxy A01 Core (Claro Argentina)

The problem

This post is a work in progress. 

If you're using a Samsung Galaxy A01 Core with a Claro Argentina SIM card, you might be frustrated by persistent pop-ups from the SIM Toolkit app appearing on your lock screen. These "flash messages" are typically promotional or service-related notifications pushed by Claro, and since can appear over the lock screen, you could "click" OK on it without noticing it (ie: if you have your phone on your pocket). The SIM Toolkit app (com.android.stk or com.android.stk2 for dual SIM devices) is a system app, meaning it cannot be uninstalled without root access. However, you can disable it or restrict its behavior using ADB (Android Debug Bridge).

 Prerequisites

  • Know what are you doing: don't blame me if you manage to brick your device. I might laugh at you and make fun of you on the net if you try to do so. Of course I am posting this because I already did it on my device and it works. Also: this procedure could work on other mobiles models or even on other mobile network providers. You just need to know what to look for.
  • A not too old desktop computer with Linux and adb already installed. All commands are issued by using a terminal like xterm.
  • USB Debugging enabled on your mobile, ie for Samsung A01Core:
    1. Go to Settings > About phone > Software information.
    2. Tap Build number 7 times to enable Developer Options.
    3. Go back to Settings > Developer options and enable USB debugging.
  • A USB cable to connect your phone to the computer.
  • Ensure your phone is recognized by ADB:
    adb devices
    
    You should see your device listed. Not just any cable can do the trick, if you don´t see your device listed, keep trying other cables until you do.

Step-by-step solution using adb

1. Identify the package

The pop-ups are likely caused by Claro Argentina's SIM Toolkit app, which is pre-installed as com.android.stk (and com.android.stk2 for dual SIM setups). Confirm this by listing installed packages:

adb shell pm list packages | grep stk

Expected output:

package:com.android.stk package:com.android.stk2

2. Disable SIM Toolkit

Since SIM Toolkit is a system app, you can't uninstall it without root, but you can disable it using ADB. Run these commands to disable both SIM Toolkit packages:

adb shell pm disable-user --user 0 com.android.stk adb shell pm disable-user --user 0 com.android.stk2

Expected output:

Package com.android.stk new state: disabled-user Package com.android.stk2 new state: disabled-user

This prevents the app from running and stops the pop-ups.

3. Force Stop (Optional)

To ensure the app stops immediately:

adb shell am force-stop com.android.stk adb shell am force-stop com.android.stk2

4. Reboot the Device

Apply the changes by rebooting your phone:

adb shell reboot

This performs a "soft reboot," restarting the Android system and applying the disabled state.

5. Check Package Status (Optional)

To confirm the packages are disabled:

adb shell pm list packages -d | grep stk

Expected output:

    package:com.android.stk
    package:com.android.stk2

If they appear here, they're disabled. To check for enabled packages:

adb shell pm list packages -e | grep stk

If nothing appears, the packages are successfully disabled.

6. How to revert

adb shell pm enable com.android.stk adb shell pm enable com.android.stk2
adb shell reboot

7. What if it does not work 

Not many options, really: switching to another rom like LineageOS (which atm does not fully support A01Core) and/or another mobile model/brand.

Notes for Claro Argentina users

  • Phone Functionality: Disabling SIM Toolkit is unlikely to affect calls, SMS, or data, as confirmed in tests on the Samsung A01 Core. However, always test after making changes.
  • Android Version: The A01 Core likely runs Android 10 (Go edition), which doesn't support the POST_NOTIFICATIONS permission (introduced in Android 13). This is why revoking notification permissions via ADB doesn't work, making disabling the app the best solution.

2025/05/20

Firefox: Prevent Google AI on web search

 grab Greasymonkey or similar

// ==UserScript==
// @name         Google Web tab redirect
// @match        https://www.google.com/search*
// @run-at       document-start
// ==/UserScript==

(function() {
    let url = new URL(location.href);
    if (!url.searchParams.has("udm")) {
        url.searchParams.set("udm", "14");
        location.replace(url.toString());
    }
})();

 

what it does: it automatically selects the "Web" option.

If tomorrow they change/remover the attribute one could simply hide it using uBlockOrigin. 


2024/04/02

slackware local mirror copy

a work in progress ...
to make sure it is working, first do a full slackpkg update. 
create /data/mirror
then run this script, adapt to your needs:

MIRROR_HOST="ftp.slackware-brasil.com.br"
MIRROR_DIR="/data/mirror"
BWLIMIT=0
RSYNC_OPTS="-avPH --bwlimit=$BWLIMIT --delete --delete-excluded --progress "

rsync $RSYNC_OPTS \
--exclude source \
$MIRROR_HOST::slackware/slackware64-current/ $MIRROR_DIR/slackware64-current
echo "Done!" 



create a vhost inside /etc/httpd/extra/http-vhosts.conf
run/restart httpd
update mirrors in /etc/slackpkg to point to the new virtual web host
run slackpkg update
should give no error

 

update:

my latest update.sh:
 

#!/bin/bash
logger inicio update
MIRROR_HOST="ftp.slackware-brasil.com.br"
MIRROR_DIR="/data/mirror"
BWLIMIT=0

RSYNC_OPTS="-aPH --bwlimit=$BWLIMIT --delete --delete-excluded "

/usr/bin/rsync $RSYNC_OPTS \
--exclude source \
$MIRROR_HOST::slackware/slackware64-current/ $MIRROR_DIR/slackware64-current
logger resultado rsync: $?

and the script to run the updates:

root@rudy2:~/bin# cat goupdate.sh  
#!/bin/bash
slackpkg update
echo listo
read
slackpkg upgrade slackpkg
slackpkg upgrade aaa_glibc-solibs
slackpkg install-new
slackpkg upgrade-all
slackpkg clean-system

The "read" was placed just in case there is no updates to do since slackpkg only report 0 on success

2023/10/05

Show "X" as Twitter in your browser

On a desktop computer (forget about cellphone), grab Firefox if you don't have it already and install FoxReplace addon. Then add a rule with the following substitutions (do not include quotes):

" X " -> " Twitter ", Match case: yes

" X:" -> " Twitter:", Match case: yes

url section can be blank which means all sites having  that text will have it replaced (ie: news)


2021/08/28

PhpUnit Code Coverage Analysis

goal is to be able to select individual directories for the CCA:

 $phpunit --coverage-html=./cca \
--coverage-filter=./app \
--coverage-filter=class   \
--coverage-filter=common  \
--coverage-filter=helpers   \
--no-configuration --color tests/testSuite.php

TODO: rant about XML.

2021/08/26

extracting PHP errors & warnings from apache log

slackware:

cd /var/log/httpd 

or  debian

cd  /var/log/apache2

clear ; cat error.log | grep PHP | grep -P  '(.*])\K.*' -o |sort -u