Determining nodes linked behind connected nodes

This might take a bit of explanation, but I'm seeking a solution to the issue of large networks being connected to a node (which itself is part of a large network). I've looked at scripting - using the connect event script to fire off a script that interrogates the node, and while I can obtain useful information from 'asterisk -r -x "rpt showvars "' and 'asterisk -r -x "rpt stats <mynode"', I don't seem to have the information I need.

I can see all nodes that are connected directly and indirectly to mine, but what I also need is which indirectly nodes are linked via which directly connected node, so I can disconnect the node that potentially could cause an issue. I already have a friendly "please don't bridge in any large networks" policy to cover the user side.

The script I'm wanting to write is intended as a "rapid intervention" to prevent/stop issues, before any more intensive manual administrative tasks (e.g. contacting users, banning nodes, etc).

How can I determine what node a large network is connected in via reliably? Using the last DTMF command is unlikely to help, as all or almost all previous incidents have been incoming connections, and comparing number of linked nodes before and after connecting doesn't cover the case where someone connects to me before connecting to a large network.

I'm looking for solutions for both ASL 1.x and 3.x (the 3.x is currently the more urgent case). Any scripting gurus able to help? Once I can obtain the information I need from AllStar, I should be able to write a Bash script to make it work. I've done similar for Echolink conferences using thebridge years ago, but Echolink has a handy "in conference" flag that I can look for, and disconnect stations which have that flag set.

Tony, this can be more complicated than it looks on the surface.
But I'm glad you understand the conditions a bit.

We all have been hit with the guy who as connected several wide networks to his node and then attempt to connect to you and perhaps your tx can't handle the duty cycle that brings.

The 2 issues for you to think about are

What if they have no connections and connect with you, then proceed to make a bunch of wide connections.
You can only reject what is connecting to you.
Even though you can look at his number of connections, if they are not there when he connects to you, it's not helping.

This node could also connect all these wide nodes to someone you are connected to.
And you can't stop that yourself. Who would you disconnect, your intended connection ?

Anyway, you gotta really put some thinking on all the implications.

I have found these type issues are caused by one person and perhaps a few nodes.
Blacklist them. Tell them not to do it.

But I do have a solution if it is always one or two same node #'s doing this.
If so, contact me off list for a solution if it meets that criteria..

On 22/6/25 10:02 am, KB8JNM-Mike via AllStarLink Discussion Groups wrote:

[Mike] Mike
June 21

Tony, this can be more complicated than it looks on the surface.
But I'm glad you understand the conditions a bit.

Probably a bit more than you realise - I'm already doing similar in the Echolink space, have done for over 20 years.

We all have been hit with the guy who as connected several wide networks to his node and then attempt to connect to you and perhaps your tx can't handle the duty cycle that brings.
My issue is more interference with major networks.

The 2 issues for you to think about are

What if they have no connections and connect with you, then proceed to make a bunch of wide connections.
You can only reject what is connecting to you.
Even though you can look at his number of connections, if they are not there when he connects to you, it's not helping.

My Echolink system uses a cron job to catch this case, so the node would be disconnected within a minute, worst case. I don't see why a similar approach couldn't be used here. However, how do I find out how many nodes are behind each connection? That's the piece of information that would allow me to write the script.

This node could also connect all these wide nodes to someone you are connected to.
And you can't stop that yourself. Who would you disconnect, your intended connection ?

True, again I can only control what connects to me. And as 99% of my connections are inbound and I have no permanent links via AllStar (the permanent links are all done via USRP, regardless of mode), disconnecting the connected node on my end is still a reasonable approach.

Anyway, you gotta really put some thinking on all the implications.

I have found these type issues are caused by one person and perhaps a few nodes.
Blacklist them. Tell them not to do it.

Email is used for repeat offenders, and recalcitrant ones have been blacklisted.

But I do have a solution if it is always one or two same node #'s doing this.
If so, contact me off list for a solution if it meets that criteria..

That sounds interesting, but as this is a hybrid web/email based system, it's more digging around to reply "off list". Time issues mean I'd have to look into that later (if I can remember).

Have you thought about walking this API https://stats.allstarlink.org/api/stats/nnnnn where nnnnn is the node number or pull the full db with wget -O allstar-stats.json https://stats.allstarlink.org/api/stats/?

I'm not particularly keen on that solution, as it means hammering the server at least once every minute. Not a good solution in that respect.

I'm honestly not sure there's a better way to do this at the moment. I don't like it either, but that's what I'm currently doing.

It would be great if an additional API endpoint could be provided to the already existing code that generates the bubble charts. Just return that same data structure in JSON instead of as the bubble chart graphic.

Stats reporting is optional. As such, I'm not convinced that using the stats API is the way to go. And, as noted, that would be increasing the load (and dependency) on the servers.

Yes, true - relying on the stats API would not be a full solution if problematic nodes are not reporting stats. It would be a good start though for nodes that have the default stats reporting settings.

For a more comprehensive solution the connected node topology would have to be determined from ASL/Asterisk itself. AMI does provide the rptStat Conn LinkedNodes list, and it would be interesting to know how it assembles that information, when it may have never received any IAX data from some of the nodes. Apparently, when an IAX connection is made there is some exchange of that information (indirectly connected node #s). And presumably there might be a way to get that data from Asterisk in a more structured way (node graph data structure rather than just a CSV list). I just opened an enhancement request to find a way to make this happen: Implement a method for AMI/app_rpt to return a connected node graph data structure · Issue #147 · AllStarLink/ASL3 · GitHub

This also gets back to the issue that keyed node data is not now sent through IAX, which is a big potential issue as has been mentioned in other posts, and which has an open enhancement request: Implement of the K: Key IAX Text · Issue #130 · AllStarLink/ASL3 · GitHub If that enhancement can be made at some point it would give a 2nd potential way to identify what node#s undesired IAX traffic was originating from and through.

Given the current IAX2 protocol use, it's not technically feasible to map out a complete graph of the network. You'd have to modify every node everywhere to make it work. Plus you have the problem of sending unexpected IAX2 metadata to non-compliant nodes. The challenge of that is ..... ultra challenging? Plus the whole point of the scheme now is that no single node needs a complete view of the whole topology.

What might be possible is at least getting back "Node N has all of the following things downstream from it" on a single node. That is all the OP really needs ... how do I snipe out the connection. You might be able to get that from AMI. I'd have go back to my processing code to see you can pull that N:M relationship out of LinkedNodes. It's data is (frustratingly) a little different that the CLI commands.

Hi,

This topic came up awhile back.

I found this very helpful: https://github.com/pcpackrat/ASL3-Mods/blob/main/killmultinodes.py

First there is a killmutlinodes.py file. Second is killmultinodes.sh file that is ran every minute via cron job to continue to monitor connected nodes (in case they decide to connect up another large system while already connected to me). The third file is the whitelist of nodes that I allow and don't disconnect if they are connected to more than just me. In this file I list every private node and AllStar Node I want to be whitelisted, with each node number on it's own separate line.

I have the same contents of the killmultinodes.sh file in the rpt.conf file so it will be triggered when another node connects to me.

killmultinodes.py

#!/usr/bin/python3

import requests
import subprocess
import argparse

def load_whitelist(file_path):
    """Loads the whitelist of node IDs from a text file."""
    try:
        with open(file_path, 'r') as file:
            # Read each line, strip any surrounding whitespace, and add to the set
            return {line.strip() for line in file}
    except FileNotFoundError:
        print(f"Whitelist file not found: {file_path}")
        return set()

def fetch_data(url):
    """Fetches data from the given URL and returns the JSON response."""
    response = requests.get(url)
    if response.status_code == 200:
        return response.json()
    else:
        print(f"Failed to retrieve data from {url}, Status Code: {response.status_code}")
        return None

def run_command(node_id):
    """Runs the system command with the given node ID."""
    command = f"asterisk -rx 'rpt fun {args.initial_node_id} *1{node_id}'"
    if not args.quiet:
        print(f"Running command: {command}")
    subprocess.run(command, shell=True)

def main():
    parser = argparse.ArgumentParser(description='Fetch and process node data.')
    parser.add_argument('initial_node_id', type=str, help='Initial Node ID to fetch data for')
    parser.add_argument('--quiet', action='store_true', help='Suppress output')
    parser.add_argument('--whitelist', type=str, help='Path to the whitelist file')
    
    global args
    args = parser.parse_args()

    # Load the whitelist from the file if provided, otherwise default to an empty set
    whitelisted_nodes = load_whitelist(args.whitelist) if args.whitelist else set()

    base_url = "https://stats.allstarlink.org/api/stats/"
    initial_url = base_url + args.initial_node_id

    # Fetch data for the initial node
    initial_data = fetch_data(initial_url)

    if initial_data:
        # Extract links
        links = initial_data.get('stats', {}).get('data', {}).get('links', [])

        if links:
            if not args.quiet:
                print(f"Node IDs connected to {args.initial_node_id}: {links}")

            # Fetch and print data for each linked node
            for node_id in links:
                node_url = base_url + str(node_id)
                node_data = fetch_data(node_url)

                if node_data:
                    if not args.quiet:
                        print(f"\nData for Node ID {node_id}:")
                    # Extract and print links for the current node
                    links_for_current = node_data.get('stats', {}).get('data', {}).get('links', [])

                    if links_for_current:
                        if not args.quiet:
                            print(f"Linked Nodes for Node ID {node_id}")
                        for linked_node_id in links_for_current:
                            if not args.quiet:
                                print(f"  Node ID: {linked_node_id}")
                            # Run the command if linked_node_id is not the initial node ID and not in the whitelist
                            if linked_node_id != args.initial_node_id and linked_node_id not in whitelisted_nodes:
                                run_command(node_id)
                    else:
                        if not args.quiet:
                            print(f"No linked nodes found for Node ID {node_id}")
                else:
                    if not args.quiet:
                        print(f"Failed to retrieve data for Node ID {node_id}")
        else:
            if not args.quiet:
                print("No links found for the initial node.")
    else:
        if not args.quiet:
            print("Failed to retrieve data for the initial node.")

if __name__ == "__main__":
    main()

killmultinodes.sh

#!/bin/bash

/usr/local/sbin/killmultinodes.py 525240 --whitelist /etc/asterisk/custom/multinodes

exit 0

Good Luck!

Found the previous post in the forums pertaining to this topic.

https://community.allstarlink.org/t/reducing-cross-linking/21634

I don't need the full topology of the network. A simple "node xxxxxx has 45 nodes connected in total" would suffice. Even better would be "what node is node yyyyyy coming via?". That way, I could disconnect anyone attempting to link in known major hubs.

Somehow AllStar's loop prevention system works it all out when one attempts to connect. It would be nice if I could simply make my node think it was already connected to those major hubs that I don't want connected directly or indirectly. In a similar vein, I'd like to be able to tell the network that I'm connected to another node that the network can't see, because that link is not via AllStar/IAX.

Looks like that hits the stats page, which was what I was hoping to avoid (and that's not a complete solution, because not all nodes report stats). I'd have to consider how I would implement that, without unduly loading the stats page. And if that page goes down, it doesn't work.