Issue with dynamic home server and duplicate server ipaddr on 3.2.8

James Wood james.wood at purplewifi.com
Mon Mar 16 20:29:38 UTC 2026


I've come across an issue with dynamic home_servers for supporting
OpenRoaming connections.

In `authorize`, we use the following code to check if it's our home domain,
if the 'home_server' already exists, or if we need to dynamically query
which radsec server to proxy to:

--------------------------------
openroaming_lookup {
        if (&control:Proxy-To-Realm || &control:Home-Server-Name) {
                return
        }

        if (&User-Name && &User-Name =~ /home.domain/) {
                return
        }
        elsif (&User-Name && &User-Name =~ /@(.*)$/) {
                switch "%{home_server_dynamic:%{1}}" {
                        case "1" {
                                # Proxy to this one particular home server
                                update control {
                                        &Home-Server-Name := "%{1}"
                                }
                        }

                        case "0" {
                                # Proxy with home server pool, failover,
etc.
                                update control {
                                        &Proxy-To-Realm := "%{1}"
                                }
                        }

                        case {
                                # no home server exists, ask DNS
                                update control {
                                        &Temp-Home-Server-String :=
`%{config:confdir}/mods-config/realm/freeradius-naptr-to-home-server.sh -d
%{config:confdir} %{1} aaa+auth:radius>
                                }
                                if (&control:Temp-Home-Server-String == ""
) {
                                        reject
                                } else {
                                        update control {
                                                &Home-Server-Name := "%{1}"
                                        }
                                }
                        }
                }
        }
}
--------------------------------

This generally works well. For a new (unknown home server), we call the
below mods-config/realm/freeradius-naptr-to-home-server.sh script to
perform the dynamic discovery:

--------------------------------
#!/bin/sh

usage() {
        echo "Usage: ${0} [OPTIONS] <realm> <optional NAPTR tag>"
        echo "        -d RADIUS_DIR       Set radius directory"
        echo "        -t                  test (skip running radmin)"
        exit 1
}

test -n "${1}" || usage

RADDB=/usr/local/etc/raddb
RADMIN=y

while [ `echo "$1" | cut -c 1` = "-" ]
do
   case "$1" in
        -d)
                RADDB=$2
                shift;shift
                ;;
        -t)
                RADMIN=
                shift
                ;;

        *)
                usage
                ;;
  esac
done

test -n "${2}" && NAPTRTAG="${2}" || NAPTRTAG="x-eduroam:radius.tls"

DIGCMD=$(command -v dig)
HOSTCMD=$(command -v host)
PRINTCMD=$(command -v printf)

validate_host() {
        echo ${@} | tr -d '\n\t\r' | grep -E '^[_0-9a-zA-Z][-._0-9a-zA-Z]*$'
}

validate_port() {
        echo ${@} | tr -d '\n\t\r' | grep -E '^[0-9]+$'
}

dig_it_srv() {
        ${DIGCMD} +short srv $SRV_HOST | sort -n -k1 |
        while read line; do
                set $line
                PORT=$(validate_port $3)
                HOST=$(validate_host $4)
                if [ -n "${HOST}" ] && [ -n "${PORT}" ]; then
                        $PRINTCMD "\tipaddr = ${HOST%.}\n\tport = ${PORT}\n"
                fi
        done
}

dig_it_naptr() {
        ${DIGCMD} +short naptr "${REALM}" | grep $NAPTRTAG | sort -n -k1 |
        while read line; do
                set $line
                TYPE=$3
                HOST=$(validate_host $6)
                if ( [ "$TYPE" = "\"s\"" ] || [ "$TYPE" = "\"S\"" ] ) && [
-n "${HOST}" ]; then
                        SRV_HOST=${HOST%.}
                        dig_it_srv
                fi
        done
}

host_it_srv() {
        ${HOSTCMD} -t srv $SRV_HOST | sort -n -k5 |
        while read line; do
                set $line
                PORT=$(validate_port $7)
                HOST=$(validate_host $8)
                if [ -n "${HOST}" ] && [ -n "${PORT}" ]; then
                        $PRINTCMD "\tipaddr ${HOST%.}:${PORT}\n"
                fi
        done
}

host_it_naptr() {
        ${HOSTCMD} -t naptr "${REALM}" | grep $NAPTRTAG | sort -n -k5 |
        while read line; do
                set $line
                TYPE=$7
                HOST=$(validate_host ${10})
                if ( [ "$TYPE" = "\"s\"" ] || [ "$TYPE" = "\"S\"" ] ) && [
-n "${HOST}" ]; then
                        SRV_HOST=${HOST%.}
                        host_it_srv
                fi
        done
}

REALM=$(validate_host ${1})
if [ -z "${REALM}" ]; then
        echo "realm \"${1}\" failed validation" >&2
        usage
fi

if [ -x "${DIGCMD}" ]; then
        SERVERS=$(dig_it_naptr)

elif [ -x "${HOSTCMD}" ]; then
        SERVERS=$(host_it_naptr)

else
        echo "${0} requires either \"dig\" or \"host\" command." >&2
        exit 1
fi

if [ ! -n "${SERVERS}" ]; then
        echo "No servers found"  >&2
        exit 1
fi

if [ -z "${RADMIN}" ]; then
        $PRINTCMD "home_server ${REALM} {\n${SERVERS}\n\t\$INCLUDE
tls.conf\n}\n"
        exit 0
fi

HOME_SERVER_FILE="$RADDB/home_servers/$1"

if [ -f "$HOME_SERVER_FILE" ]; then
    echo "Home server file $HOME_SERVER_FILE already exists. Skipping
creation and radmin command." >&2
else
    echo "Creating new home server file $HOME_SERVER_FILE." >&2
    $PRINTCMD "home_server ${REALM} {\n${SERVERS}\n\t\$INCLUDE
tls.conf\n}\n" > "$HOME_SERVER_FILE"

    /usr/local/sbin/radmin -e "add home_server file $HOME_SERVER_FILE"
fi

echo $1
--------------------------------

Again, this works well. It creates a new file in the home_servers folder,
and radmin adds it to the server to use immediately.

The problem arises if we have an authentication request from a new realm
but the "server" is one that we already have a reference to, from a
previously added dynamic home server definition.

For example, an authentication request comes in with a realm of
davidlloyd.openroaming.net and the script generates the following home
server config:

# cat home_servers/davidlloyd.openroaming.net
home_server davidlloyd.openroaming.net {
      ipaddr = idpeu.openroaming.net
      port = 2083
      $INCLUDE tls.conf
}

This new home server was then added via radmin, and everything was ok. The
request is proxied to that new server. So far, so good.

Later, another authentication request comes in with a realm of
eu-sdk.openroaming.net, and the script generates the following home server
config:

# cat home_servers/eu-sdk.openroaming.net
home_server eu-sdk.openroaming.net {
      ipaddr = idpeu.openroaming.net
      port = 2083
      $INCLUDE tls.conf
}

However, this failed to add via radmin, because the "ipaddr" is the same as
a previously defined dynamic home server, and the authentication fails as
it doesn't proxy the request.

      ipaddr = idpeu.openroaming.net <<<< this

The error thrown by radmin is:

# radmin -e "add home_server file /usr/local/etc/raddb/home_servers/
davidlloyd.openroaming.net"
ERROR: Unable to add home server - Failed adding home_server to the
internal data structures

This doesn't just happen for "openroaming" hosted realms either. Take
cityroam in Japan for example. They have multiple realms too, but the same
servers:

# cat home_servers/*city*

home_servers/jwa.bemap.cityroam.jp:

 home_server jwa.bemap.cityroam.jp {
      ipaddr = jpgw4.cityroam.jp
      port = 2083
      ipaddr = jpgw.cityroam.jp
      port = 2083
      $INCLUDE tls.conf
}

home_servers/w-jp1.wi2.cityroam.jp:

 home_server w-jp1.wi2.cityroam.jp {
      ipaddr = jpgw4.cityroam.jp
      port = 2083
      ipaddr = jpgw.cityroam.jp
      port = 2083
      $INCLUDE tls.conf
}

It's like we need a way to set servers and then map multiple realms to the
same server (as traditionally done in proxy.conf)?

How can we best solve this issue? Is there another method for doing this
besides the one above?

We can't statically define these servers (as the whole point of openroaming
is to dynamic proxy to the resolved servers, which may change), and we'll
never know what user realm may hit us next will be, and they will probably
share servers.

Thanks,

James

-- 


Visit purple.ai <https://purple.ai/>

Purple on LinkedIn 
<https://uk.linkedin.com/company/purple-wifi>

Email disclaimer 
<http://www.purple.ai/email-disclaimer/>


More information about the Freeradius-Users mailing list