#!/bin/sh # Script ajout de routes VM # A CONFIGURER/VERIFIER : # - USER_IPAM # - PASSWORD_IPAM # - ID_RANGES_V4 # - ID_RANGES_V6 # - LOG_PLACE # - CHEMIN_CACHE write_log() { # $1: log level (INFO, ERROR, etc.) # $2: message printf "$(date -Isecond) - $1: $2\n" >> \ ${LOG_PLACE}/$(date -Idate)_vm_routing.log if [ $1 = 'ERROR' ] || [ $1 = 'FATAL' ] || [ $1 = 'WARN' ]; then printf "$1: $2\n" fi } # IPAM creds CREDS_FILE='/etc/creds' if [ -f $CREDS_FILE ]; then . $CREDS_FILE CURL='curl -s -k -sS' else write_log FATAL 'The file creds doesn’t exist' exit 1 fi # check for commands in the path for bin in curl ip ipv6calc jq; do if [ -z $(which ${bin} 2>/dev/null) ]; then MISSING="${MISSING} ${bin}" fi done if [ ! -z "${MISSING}" ]; then printf "You’r missing this:${MISSING}\n" exit 1 fi # Chemin fichier de LOG (et création du répertoire s'il n'existe pas) LOG_PLACE='/var/local/log/vm_id_log/' if [ ! -d "${LOG_PLACE}" ];then mkdir -p ${LOG_PLACE} write_log INFO 'Creation of the log dir' fi write_log INFO 'Beginning of the script execution' # Chemin fichier de CACHE (et création du répertoire s'il n'existe pas) CHEMIN_CACHE='/var/local/cache/vm_id_cache/' if [ ! -d "${CHEMIN_CACHE}" ];then mkdir -p ${CHEMIN_CACHE} write_log INFO 'Creation of the cache dir' fi # Fonction d'aide usage() { printf "Usage: ./creationRoutesVM.sh \n" printf "VM_ID est l'id d'une VM dans PROXMOX, un entier supérieur à zéro\n" printf "option : \t-h, --help Affiche ce message d’aide\n" } # On vérifie la bonne présence d'un paramètre VM_ID if [ $1 ]; then if [ $1 = '-h' ] || [ $1 = '--help' ]; then usage exit 0 elif [ $# -lt 1 ]; then write_log FATAL 'VM ID not specified' usage exit 1 else # Verification of the VM ID expr $1 + 0 1>/dev/null 2>&1 if [ $? -lt 2 ] && [ $1 -gt 0 ]; then VM_ID=$1 else write_log FATAL 'The VM ID must a non-null integer' exit 1 fi fi else write_log FATAL 'No parameter specified' usage exit 1 fi IFACE="tap${VM_ID}i0" IP4_NEIGH="$(ip -4 neigh show dev ${IFACE})" IP6_NEIGH="$(ip -6 neigh show dev ${IFACE})" IP4_ROUTE="$(ip -4 route show dev ${IFACE})" IP6_ROUTE="$(ip -6 route show dev ${IFACE})" if [ "${IP4_NEIGH}" ] && [ "${IP6_NEIGH}" ] && [ "${IP4_ROUTE}" ] && [ "${IP6_ROUTE}" ]; then write_log INFO 'Nothing to do' exit 0 fi # Récupération d'un TOKEN pour utiliser IPAM via l'API write_log DEBUG 'Connecting to API' RES_AUTHENT=$(${CURL} -X POST --user ${USER_IPAM}:${PASSWORD_IPAM} ${URL}/user/) CODE_RETOUR_RES_AUTHENT=$(echo ${RES_AUTHENT} | jq '.code') # Si OK, on utilise l'IPAM, sinon on passe par le fichier de cache if [ ${CODE_RETOUR_RES_AUTHENT} != 200 ]; then write_log ERROR "Wrong HTTP code from the API: ${CODE_RETOUR_RES_AUTHENT}" write_log INFO "Using cache instead" else FEED_CACHE=1 fi # Mise du TOKEN dans une variable et retrait des éventuelles guillemets ajoutées lors de la récupération via jq if [ ${FEED_CACHE} = 1 ]; then write_log DEBUG 'Getting token' TOKEN=$(echo ${RES_AUTHENT} | jq -r '.data.token') if [ ! -n "${TOKEN}" ]; then write_log FATAL 'Empty token' exit 1 fi write_log INFO 'Feeding cache' # IPv4 addresses for ID_RANGE_V4 in ${ID_RANGES_V4}; do # Récupération des informations V4 et vérification du code HTTP INFOS_V4=$(${CURL} --header "Content-type: application/x-www-form-urlencoded" --header "token: $TOKEN" -X GET "${URL}/subnets/${ID_RANGE_V4}/addresses") CODE_RETOUR_INFOS_V4=$(echo ${INFOS_V4} | jq '.code') if [ ${CODE_RETOUR_INFOS_V4} != 200 ]; then write_log FATAL "Wrong code returned for IPv4 range ${ID_RANGE_V4}: ${CODE_RETOUR_INFOS_V4}" exit 1 fi # Mise à jour du fichier de cache pour ce ID de subnet echo ${INFOS_V4} > ${CHEMIN_CACHE}/${ID_RANGE_V4} done # Freeing RAM INFOS_V4='' CODE_RETOUR_INFOS_V4='' # IPv6 ranges for ID_RANGE_V6 in ${ID_RANGES_V6}; do # Récupération des informations V6 et vérification du code HTTP INFOS_RANGES_V6=$(${CURL} --header "Content-type: application/x-www-form-urlencoded" --header "token: $TOKEN" -X GET ${URL}/subnets/${ID_RANGE_V6}/slaves/) CODE_RETOUR_INFOS_RANGES_V6=$(echo ${INFOS_RANGES_V6} | jq '.code') if [ ${CODE_RETOUR_INFOS_RANGES_V6} != 200 ]; then write_log FATAL "Wrong code returned for IPv4 range ${ID_RANGE_V6}: ${INFOS_RANGES_V6}" exit 1 fi echo ${INFOS_RANGES_V6} > ${CHEMIN_CACHE}/${ID_RANGE_V6} done # Freeing RAM INFOS_RANGES_V6='' CODE_RETOUR_INFOS_RANGES_V6='' fi MAC=$(grep 'vmbr1$' /etc/pve/nodes/*/qemu-server/${VM_ID}.conf | \ sed -E 's/.*([[:xdigit:]:]{17}).*/\1/') # Pour chaque subnet V4 for ID_RANGE_V4 in ${ID_RANGES_V4}; do # Récupération des informations V4 en cache pour cet ID de subnet si le fichier de cache associé existe if [ ! -f "${CHEMIN_CACHE}/${ID_RANGE_V4}" ]; then write_log FATAL "File not found: ${CHEMIN_CACHE}/${ID_RANGE_V4}" exit 1 fi VM_IPV4=$(jq -r ".data[] | select(.custom_vm_id==\"${VM_ID}\") | .ip" ${CHEMIN_CACHE}/${ID_RANGE_V4}) # Si la valeur n'est pas vide et correspond à une IPV4 if [ -n "${VM_IPV4}" ]; then # Ajout de la route associée COMMAND="ip -4 route add ${VM_IPV4}/32 dev ${IFACE}" echo "${COMMAND}" write_log INFO "${COMMAND}" ${COMMAND} # Updating ARP COMMAND="ip neigh add ${VM_IPV4} lladdr ${MAC} dev ${IFACE}" echo "${COMMAND}" write_log INFO "${COMMAND}" ${COMMAND} # If the previous command fails, it’s because trafic has # already be sent to the VM if [ $? = 2 ]; then ip neigh replace ${VM_IPV4} lladdr ${MAC} dev ${IFACE} fi GW_IPv4=$(jq -r ".data[] | select(.is_gateway==\"1\") | .ip" ${CHEMIN_CACHE}/${ID_RANGE_V4}) else # En cas d'echec, on continue de parcourir les autres RANGES write_log WARN 'Empty value or doen’t correspond to an IPv4' fi done # Pour chaque subnet V6 for ID_RANGE_V6 in ${ID_RANGES_V6}; do # Récupération des informations V6 en cache pour cet ID de subnet si le fichier de cache associé existe if [ ! -f "${CHEMIN_CACHE}/${ID_RANGE_V6}" ]; then write_log FATAL "File not found: ${CHEMIN_CACHE}/${ID_RANGE_V4}" exit 1 fi # Recherche des informations correspondants à la VM_ID VM_IPV6=$(jq -r ".data[] | select(.custom_vm_id==\"${VM_ID}\") | .subnet" ${CHEMIN_CACHE}/${ID_RANGE_V6}) VM_NH=$(jq -r ".data[] | select(.custom_vm_id==\"${VM_ID}\") | .\"Next-hop\"" ${CHEMIN_CACHE}/${ID_RANGE_V6}) # Si la valeur n'est pas vide et correspond à un range IPV6 if [ -n "${VM_IPV6}" ]; then if [ ${VM_NH} = 'null' ]; then VM_NH=$(ipv6calc --action prefixmac2ipv6 --in prefix+mac --out ipv6addr fe80:: ${MAC}) fi # Activation of IPv6 on the tap interface sysctl net.ipv6.conf.${IFACE}.disable_ipv6=0 # Ajout de la route associée COMMAND="ip -6 route add ${VM_IPV6}/48 via ${VM_NH} dev ${IFACE}" echo "${COMMAND}" write_log INFO "${COMMAND}" ${COMMAND} # Updating NDP COMMAND="ip neigh add ${VM_NH} lladdr ${MAC} dev ${IFACE}" echo "${COMMAND}" write_log INFO "${COMMAND}" ${COMMAND} # If the previous command fails, it’s because trafic has # already be sent to the VM if [ $? = 2 ]; then ip neigh replace ${VM_NH} lladdr ${MAC} dev ${IFACE} fi GW_IPv6="fe80::204:92:100:1" else # En cas d'echec, on continue de parcourir les autres RANGES echo "Valeur vide ou ne correspond pas à un range IPV6" fi done # Send ICMP to update ARP/NDP cache send-icmp.py ${MAC} ${IFACE} ${VM_IPV4} ${GW_IPv4} ${VM_NH} ${GW_IPv6} write_log INFO 'Script ended' exit 0