#!/bin/bash

# Easily set up a spoofed MAC address to a network interface on systemd-based Linux systems.
# Based on: https://wiki.archlinux.org/index.php/MAC_address_spoofing#Method_1:_systemd-networkd

# Copyright: Pekka Helenius (~Fincer), 2018

# Randomizing/Modifying MAC address may disturb malicious ARP scans (device idenfitication) and may help circumventing MAC-based restrictions which may be present on the local network.
# Use only at your own risk. Spoofing MAC address may not be accepted in all situations, may be illegal and may cause legal troubles. You have been warned.

#genmac() {

    trap ctrl_c INT

    function ctrl_c() {
            echo -e "\nAborting.\n"
            exit
            #return
    }

    random_mac() {
		# Author: Aaron Toponce
		# https://serverfault.com/questions/299556/how-to-generate-a-random-mac-address-from-the-linux-command-line/631119#631119
        MAC=$(printf '%02x' $((0x$(od /dev/urandom -N1 -t x1 -An | cut -c 2-) & 0xFE | 0x02)); od /dev/urandom -N5 -t x1 -An | sed 's/ /:/g')
    }

    insert_mac() {
        echo -e "\nChanging MAC address information (root permission required).\n"
        
        sudo sed -i "/\[Link\]/!b;n;cMACAddress=$MAC" /etc/systemd/network/00-default.link
        echo -e "MAC address changed from '$MAC_OLD' to '$MAC' for interface '$MAC_DEVICE'.\n\nPlease restart this interface to apply the changes.\n\nTo restore original MAC address, either delete configuration file '/etc/systemd/network/$linkname' or set real MAC address manually into it.\n"
        exit 0
        #return 0
    }

    geninsert_mac() {

        gennew_mac() {
        
            while true; do
                unset response
                read -r -p "Generate a new MAC address? [Y/n] " response
                
                if [[ $(echo $response | sed 's/ //g') =~ ^([yY][eE][sS]|[yY])$ ]]; then
                    random_mac
                    newname_mac
                else
                    echo -e "\nKeeping old MAC address configuration.\n"
                    exit
                    #return
                fi
            done
        }

        newname_mac() {
            unset response
            read -r -p "New MAC address for '$MAC_DEVICE' will be '$MAC'. Accept? [Y/n] " response
        
            if [[ $(echo $response | sed 's/ //g') =~ ^([yY][eE][sS]|[yY])$ ]]; then
                insert_mac
            else
                gennew_mac
            fi
        }
        newname_mac
    }

    gen_mac() {

        real_mac() {

            AVAILABLE_MACS=$(ip -br link show | sed '/LOOPBACK/d' | awk '{print NR"\t"$1"\t"$3"\t"$2}')
            
            IFS=$'\n'

            echo -e "\nAvailable network interfaces with their MAC addresses are:\n\n${AVAILABLE_MACS[*]}"
            
            echo -e "\nPlease select the interface which MAC address you want to spoof of\n"
            read -r -p "Selection [number]: " number
            
            if [[ ! $number =~ ^[0-9]+$ ]]; then
                echo -e "\nInvalid input value. Aborting.\n"
                exit 1
                #return 1
            fi
            
            for INTERFACE in $(echo -e "${AVAILABLE_MACS[*]}"); do
            
                intf_num=$(echo $INTERFACE | awk '{print $1}')

                if [[ $number -eq $intf_num ]]; then
                    MAC_REAL=$(echo $INTERFACE | awk '{print $3}')
                    MAC_DEVICE=$(echo $INTERFACE | awk '{print $2}')
                    break
                fi
            done
            unset IFS

            if [[ $MAC_REAL == "" ]]; then
                echo -e "\nNot a valid MAC address found for interface number $number. Aborting.\n"
                exit 1
                #return 1
            fi

        }

        real_mac

        PREV_CONF=$(grep -Ril "$MAC_REAL" /etc/systemd/network/ | grep -E "[0-9]{,2}\-default\.link$")
        
        if [[ ! $(echo $PREV_CONF | wc -w) -eq 0 ]]; then 
            echo -e "\nUsing existing configuration file for interface '$MAC_DEVICE':\n$PREV_CONF\n"
            linkname=$(basename $PREV_CONF)
            MAC_OLD=$(awk -F= '/\[Link\]/{getline; print $2}' $PREV_CONF)
        else
            MAC_OLD=$MAC_REAL
            echo -e "\nPrevious configuration file not found. Creating it (root permission required).\n"
            read -r -p "Configuration file name? (must follow syntax: 00-default.link, 41-default.link, 98-default.link etc.): " linkname

            if [[ "$linkname" =~ ^[0-9]{,2}\-default\.link$ ]]; then
            
                if [[ ! $(sudo -n true) ]]; then
                    sudo echo ""
                fi
            
                echo -e "[Match]\nMACAddress=$MAC_REAL\n\n[Link]\nMACAddress=$MAC_REAL\nNamePolicy=kernel database onboard slot path" \
                | sudo tee /etc/systemd/network/$linkname > /dev/null
                
                echo -e "Created new configuration file: /etc/systemd/network/$linkname\n"
            else
                echo -e "\nInvalid file name given. Aborting.\n"
                exit 1
            fi
        fi

        unset response
        echo -e "Either randomly generated or manually specified MAC address can be used.\n"
        read -r -p "Do you want to use randomly generated MAC address? [Y/n] " response

        if [[ $(echo $response | sed 's/ //g') =~ ^([yY][eE][sS]|[yY])$ ]]; then
            random_mac
            geninsert_mac
        else
        
            if [[ $(echo $response | sed 's/ //g') =~ ^([nN][oO]|[nN])$ ]]; then
                read -r -p "Please type a new MAC address (Syntax is e.g. aa:bb:33:zz:f0:4a): " MAC
                maxtries=5
                while [[ $maxtries -gt 0 ]]; do

                    case "$MAC" in 
                    [[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]])
                        insert_mac
                        ;;
                    esac
                    unset MAC
                    read -r -p "Invalid MAC address given. Please type again ($maxtries tries left): " MAC
                    let maxtries--
                done
            else
                echo -e "\nInvalid answer. Aborting.\n"
            fi
        fi
    }

    echo -e "\nWARNING: Changing MAC address WILL DISRUPT connections to any network device using MAC-based authentication methods. These devices may include configured routers, WiFi hotspots etc. Remember to write down the new MAC address, and make sure you are authorized to configure the MAC address to all affected network devices if needed.\n"
    read -r -p "You are going to spoof a MAC address of this computer. Are you sure? [Y/n] " response

    if [[ $(echo $response | sed 's/ //g') =~ ^([yY][eE][sS]|[yY])$ ]]; then
        gen_mac
    else
        echo -e "\nKeeping old MAC address configuration.\n"
    fi

#}
