Aktionen

PhpIPAM MAC-Adressen per arp ermitteln und hinzufügen: Unterschied zwischen den Versionen

Aus znilwiki

Zeile 194: Zeile 194:
#echo "[INFO] Removed token"
#echo "[INFO] Removed token"
</source>
</source>
<br>
----
==Konfiguration==
URL='https://phpipam.znil.local/api'
APP='ARP-CronJob'
#USER='user'
#PASS='passwort'
TOKEN='dI4iAJlvuo9z78xlCUt5N4ubmy1FPyoL'
SUBNET='192.168.0.0/22'
INTERFACE='ens33'
INVALIDTAGS=("3" "4")

Version vom 19. Mai 2025, 10:56 Uhr

Changelog:

  • 19.05.2025 erste Version

Vorwort

phpIPAM kann zu den IP-Adressen auch die MAC-Adressen zuordnen:


Dazu wird jedoch ausschließlich SNMP verwendet. Man muss ein Gerät vom Typ Switch einrichten und die Abfrage der MAC-Adress-Tabelle per SNMP einrichten.
Das macht ja durchaus sinn, jedoch sind meine Allnet-Switche zum Beispiel nicht kompatibel damit.

Im Internet fand ich dieses Skript hier:

https://github.com/lazynooblet/scripts/tree/main/bash/phpipam-update-mac-from-arp

welches die ARP-Tabelle auf dem Linux-System nutzt vom welches es aufgerufen wird.
Ich habe das Skript in 2 Punkten abgepasst:

  • Im Original wird sich per Benutzername und Passwort angemeldet um sich den API-Key zu holen. Meine Variante nutzt einfach gleich den API Key (und versucht auch nicht diesen am Ende zu löschen)
  • Ich lasse vorher einen fping über den Bereich laufen damit der ARP-Cache auch frisch gefüllt ist.



Das geänderte Skript

nano phpipam-update-mac-from-arp_V1.1.sh
#!/bin/bash

# V1.1
# Original by "lazynooblet" => https://github.com/lazynooblet/scripts/tree/main/bash/phpipam-update-mac-from-arp
# Modified 2025-05-19 by Bernhard Linz => https://znil.net/index.php?title=PhpIPAM_MAC-Adressen_per_arp_ermitteln_und_hinzuf%C3%BCgen
# Changes V1.1:
# - Remove USER and PASSWORD Authentification, use TOKEN instead
# - Add fping before checking MAC-Adresses to fill up ARP-Cache

URL='https://phpipam.znil.local/api'
APP='ARP-CronJob'
#USER='user'
#PASS='passwort'
TOKEN='dI4iAJlvuo9z78xlCUt5N4ubmy1FPyoL'

SUBNET='192.168.0.0/22'
INTERFACE='ens33'
INVALIDTAGS=("3" "4")

#echo "[INFO] Fetching token"
#TOKEN=$(curl -X POST --user ${USER}:${PASS} ${URL}/${APP}/user/ -s | jq --raw-output .data.token)
#if [ "$?" != "0" ]; then
#    echo "[FAIL] Error fetching token from PHPIPAM" 1>&2
#    exit 1
#fi
#if [ -z "${TOKEN}" ]; then
#    echo "[FAIL] Didn't receive valid token from PHPIPAM" 1>&2
#    exit 1
#fi

#echo "[INFO] Fetching token expiry (token test)"
#EXPIRES=$(curl -X GET --header "token: ${TOKEN}" ${URL}/${APP}/user/ -s | jq --raw-output .data.expires)
#if [ "$?" != "0" ]; then
#    echo "[FAIL] Error fetching token expiry from PHPIPAM" 1>&2
#    exit 1
#fi
#if [ -z "${EXPIRES}" ]; then
#    echo "[FAIL] Didn't receive valid token expiry from PHPIPAM" 1>&2
#    exit 1
#fi
echo "[INFO] fping subnet"
fping -4 -a -q -g ${SUBNET}

echo "[INFO] Fetching subnet id"
SUBNETID=$(curl -X GET --header "token: ${TOKEN}" ${URL}/${APP}/subnets/search/${SUBNET} -s | jq --raw-output .data[0].id)
if [ "$?" != "0" ]; then
    echo "[FAIL] Error fetching subnet id from PHPIPAM" 1>&2
    exit 1
fi
if [ -z "${SUBNETID}" ]; then
    echo "[FAIL] Didn't receive valid subnet id from PHPIPAM" 1>&2
    exit 1
fi

_update() {
    ip=${1}
    mac=${2}

    IPID=$(curl -X GET --header "token: ${TOKEN}" ${URL}/${APP}/addresses/${ip}/${SUBNETID} -s | jq --raw-output .data.id)
    if [ "$?" != "0" ]; then
        echo "[FAIL] Error fetching ip id from PHPIPAM" 1>&2
        exit 1
    fi
    if [ -z "${IPID}" ] || [ "${IPID}" = "null" ]; then
        echo "[INFO] address not in phpipam database: ${ip}"
        return
    fi

    NOUPDATE=0
    JSON=$(curl -X GET --header "token: ${TOKEN}" ${URL}/${APP}/addresses/${IPID}/ -s)
    if [ "$?" != "0" ]; then
        echo "[FAIL] Error fetching ip data from PHPIPAM" 1>&2
        exit 1
    fi
    IPTAG=$(echo ${JSON} | jq --raw-output .data.tag)
    for tag in ${INVALIDTAGS[@]}; do
        if [ "${IPTAG}" = "${tag}" ]; then
            echo "[INFO] refusing to update ip with tag: ${IPTAG}"
            NOUPDATE=1
        fi
    done
    if [ ${NOUPDATE} -eq 1 ]; then return; fi

    IPMAC=$(echo ${JSON} | jq --raw-output .data.mac)
    if [ -z "${IPMAC}" ]; then
        echo "[INFO] No mac set for ip"
    fi
    if [ "${IPMAC}" = "${mac}" ]; then
        echo "[INFO] no update required for mac"
        return
    fi

    JSON=$(curl -X PATCH --header "token: ${TOKEN}" ${URL}/${APP}/addresses/${IPID}/ -s --data "mac=${mac}")
    if [ "$?" != "0" ]; then
        echo "[FAIL] Error updating ip mac on PHPIPAM" 1>&2
        exit 1
    fi
    RESULT=$(echo ${JSON} | jq --raw-output .success)
    if [ "${RESULT}" != "true" ]; then
        echo "[FAIL] API returned error updating ip on PHPIPAM: ${JSON}"
        exit 1
    fi

    echo "[INFO] Updated id:${IPID} ip:${ip} mac:${mac}"
}

while read a b c d e; do
    # ? (192.168.1.10) at a1:b2:c3:d4:e5:f6 [ether] on eth0
    line="${a} ${b} ${c} ${d} ${e}"
    ip=$(echo ${b} | sed 's/[^0-9\.]//g')
    mac=${d}

    if [ "${mac}" = "<incomplete>" ]; then
        # we silently skip these
        continue
    fi

    if ! [[ ${mac} =~ ^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$ ]]; then
        echo "[INFO] Invalid mac: ${mac}"
        continue
    fi

    echo "[INFO] Found ip:${ip} mac:${mac}"
    if [ -z "${ip}" ]; then
        echo "[WARN] Unable to parse ip from '${b}': ${line}" 1>&2
    fi
    if [ -z "${mac}" ]; then
        echo "[WARN] Unable to parse mac from '${d}': ${line}" 1>&2
    fi

    _update ${ip} ${mac}

done < <(arp -an -i ${INTERFACE})
if [ "$?" != "0" ]; then
    echo "[FAIL] Error running arp" 1>&2
    exit 1
fi

# get json output from ip for local interfaces
IPJSON=$(ip -j a show ${INTERFACE})
if [ "$?" != "0" ]; then
    echo "[FAIL] Error running ip a show" 1>&2
    exit 1
fi
localmac=$(echo ${IPJSON} | jq --raw-output '.[0].address')

if [[ ${localmac} =~ ^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$ ]]; then
    LENGTH=$(echo ${IPJSON} | jq --raw-output '.[0].addr_info | length')
    for (( i=0; i<${LENGTH}; i++)); do
        localip=$(echo ${IPJSON} | jq --raw-output .[0].addr_info[${i}].local)
        if [ -z "${localip}" ]; then
            echo "[WARN] Unable to parse ip [${i}] from '${IPJSON}'" 1>&2
            continue
        fi
        echo "[INFO] Found interface:${INTERFACE} ip:${localip} mac:${localmac} index:$((${i} +1))/${LENGTH}"
        _update ${localip} ${localmac}
    done
else
    echo "[INFO] Invalid mac from interface: ${localmac}"
fi



#JSON=$(curl -X DELETE --header "token: ${TOKEN}" ${URL}/${APP}/user/ -s)
#if [ "$?" != "0" ]; then
#    echo "[FAIL] Error removing token" 1>&2
#    exit 1
#fi
#RESULT=$(echo ${JSON} | jq --raw-output .success)
#if [ "${RESULT}" != "true" ]; then
#    echo "[FAIL] API returned error when attempting to remove token: ${JSON}"
#    exit 1
#fi
#echo "[INFO] Removed token"



Konfiguration

URL='https://phpipam.znil.local/api' APP='ARP-CronJob'

  1. USER='user'
  2. PASS='passwort'

TOKEN='dI4iAJlvuo9z78xlCUt5N4ubmy1FPyoL'

SUBNET='192.168.0.0/22' INTERFACE='ens33' INVALIDTAGS=("3" "4")