在shell脚本中展开ipv6地址

u5i3ibmn  于 2023-05-07  发布在  Shell
关注(0)|答案(6)|浏览(564)

我想根据给定的IPv6地址更新djbdns(dbndns)配置文件,例如2a01:488:66:1000:523:f116:0:1::1
dbndns需要扩展的IPv6地址,例如2a010488006610000523f116000000012a01:488:66:1000:523:f116:0:1
扩展此类IPv6地址的最简单方法是什么?

cnwbcb6i

cnwbcb6i1#

使用sipcalc可以做到这一点。它提供了比你需要的更多的信息,但是一点grepcut可以解决这个问题:-)

$ EXPANDED=`sipcalc 2001::1 | fgrep Expanded | cut -d '-' -f 2`
$ echo $EXPAND
2001:0000:0000:0000:0000:0000:0000:0001

作为参考,这是sipcalc的完整输出:

$ sipcalc 2001::1
-[ipv6 : 2001::1] - 0

[IPV6 INFO]
Expanded Address        - 2001:0000:0000:0000:0000:0000:0000:0001
Compressed address      - 2001::1
Subnet prefix (masked)  - 2001:0:0:0:0:0:0:1/128
Address ID (masked)     - 0:0:0:0:0:0:0:0/128
Prefix address          - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
Prefix length           - 128
Address type            - Aggregatable Global Unicast Addresses
Network range           - 2001:0000:0000:0000:0000:0000:0000:0001 -
                          2001:0000:0000:0000:0000:0000:0000:0001
bvuwiixz

bvuwiixz2#

我最近想要一个无依赖性的解决方案,它可以跨shell移植,并可以在openwrt等平台上工作。我想出了以下片段:

# helper to convert hex to dec (portable version)
hex2dec(){
    [ "$1" != "" ] && printf "%d" "$(( 0x$1 ))"
}

# expand an ipv6 address
expand_ipv6() {
    ip=$1

    # prepend 0 if we start with :
    echo $ip | grep -qs "^:" && ip="0${ip}"

    # expand ::
    if echo $ip | grep -qs "::"; then
        colons=$(echo $ip | sed 's/[^:]//g')
        missing=$(echo ":::::::::" | sed "s/$colons//")
        expanded=$(echo $missing | sed 's/:/:0/g')
        ip=$(echo $ip | sed "s/::/$expanded/")
    fi

    blocks=$(echo $ip | grep -o "[0-9a-f]\+")
    set $blocks

    printf "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n" \
        $(hex2dec $1) \
        $(hex2dec $2) \
        $(hex2dec $3) \
        $(hex2dec $4) \
        $(hex2dec $5) \
        $(hex2dec $6) \
        $(hex2dec $7) \
        $(hex2dec $8)
}

我还有这个功能可以压缩

# returns a compressed ipv6 address under the form recommended by RFC5952
compress_ipv6() {
    ip=$1

    blocks=$(echo $ip | grep -o "[0-9a-f]\+")
    set $blocks

    # compress leading zeros
    ip=$(printf "%x:%x:%x:%x:%x:%x:%x:%x\n" \
        $(hex2dec $1) \
        $(hex2dec $2) \
        $(hex2dec $3) \
        $(hex2dec $4) \
        $(hex2dec $5) \
        $(hex2dec $6) \
        $(hex2dec $7) \
        $(hex2dec $8)
    )

    # prepend : for easier matching
    ip=:$ip

    # :: must compress the longest chain
    for pattern in :0:0:0:0:0:0:0:0 \
            :0:0:0:0:0:0:0 \
            :0:0:0:0:0:0 \
            :0:0:0:0:0 \
            :0:0:0:0 \
            :0:0; do
        if echo $ip | grep -qs $pattern; then
            ip=$(echo $ip | sed "s/$pattern/::/")
            # if the substitution occured before the end, we have :::
            ip=$(echo $ip | sed 's/:::/::/')
            break # only one substitution
        fi
    done

    # remove prepending : if necessary
    echo $ip | grep -qs "^:[^:]" && ip=$(echo $ip | sed 's/://')

    echo $ip
}

您可以将它们组合起来测试给定的输入是否是ipv6。

# a valid ipv6 is either the expanded form or the compressed one
is_ipv6(){
    expanded="$(expand_ipv6 $1)"
    [ "$1" = "$expanded" ] && return 0
    compressed="$(compress_ipv6 $expanded)"
    [ "$1" = "$compressed" ] && return 0
    return 1
}

我希望这有帮助!这些代码片段取自https://github.com/chmduquesne/wg-ip。如果您发现任何bug,请贡献!

klr1opcd

klr1opcd3#

__rfc5952_expand () {
    read addr mask < <(IFS=/; echo $1)
    quads=$(grep -oE "[a-fA-F0-9]{1,4}" <<< ${addr/\/*} | wc -l)
    #[ "${addr:${#addr}-1}" == ":" ] && { addr="${addr}0000"; (( quads++ )); }
    grep -qs ":$" <<< $addr && { addr="${addr}0000"; (( quads++ )); }
    grep -qs "^:" <<< $addr && { addr="0000${addr}"; (( quads++ )); }
    [ $quads -lt 8 ] && addr=${addr/::/:$(for (( i=1; i<=$(( 8 - quads )) ; i++ )); do printf "0000:"; done)}
    #addr=$(
    #for quad in $(IFS=:; echo ${addr}); do
    #    [ "${#quad}" -lt 4 ] && for (( i=${#quad}; i<4 ; i++ )); do quad=0${quad}; done
    #    printf "${delim}${quad}"; delim=":";
    # Or so if you need result without colon, as asked in first post
    #   printf "${quad}";
    #done)
    addr=$(for quad in $(IFS=:; echo ${addr}); do printf "${delim}%04x" "0x${quad}"; delim=":"; done)
    #addr=$(for quad in $(IFS=:; echo ${addr}); do printf "%04x" "0x${quad}"; done)  
    [ ! -z $mask ] && echo $addr/$mask || echo $addr
}

for ip in 2a01:4f8:211:9e::/64 ::1/128; do __rfc5952_expand $ip; done

2a01:04f8:0211:009e:0000:0000:0000:0000/64
0000:0000:0000:0000:0000:0000:0000:0001/128

__rfc5952_compact () {
    read addr mask < <(IFS=/; echo $1)
    addr=$(for quad in $(IFS=:; echo ${addr}); do printf "${delim}%x" "0x${quad}"; delim=":"; done)
    for zeros in $(grep -oE "((^|:)0)+:?" <<< $addr | sort -r | head -1); do addr=${addr/$zeros/::}; done
    [ ! -z $mask ] && echo $addr/$mask || echo $addr
}

for ip in 2a01:04f8:0211:009e:00:0001:0000:0000/64 0000:0000:0000:0000:0000:0000:0000:0001/128; do __rfc5952_compact $ip; done

2a01:4f8:211:9e:0:1::/64
::1/128
euoag5mw

euoag5mw4#

使用awk,你可以这样做:

$ echo 2001::1 | awk '{if(NF<8){inner = "0"; for(missing = (8 - NF);missing>0;--missing){inner = inner ":0"}; if($2 == ""){$2 = inner} else if($3 == ""){$3 = inner} else if($4 == ""){$4 = inner} else if($5 == ""){$5 = inner} else if($6 == ""){$6 = inner} else if($7 == ""){$7 = inner}}; print $0}' FS=":" OFS=":" | awk '{for(i=1;i<9;++i){len = length($(i)); if(len < 1){$(i) = "0000"} else if(len < 2){$(i) = "000" $(i)} else if(len < 3){$(i) = "00" $(i)} else if(len < 4){$(i) = "0" $(i)} }; print $0}' FS=":" OFS=":"
$ 2001:0000:0000:0000:0000:0000:0000:0001

对awk的第一次调用在“::“之间添加缺少的零和冒号;对awk的第二次调用将缺少的0添加到每个组。
要修剪冒号,只需将最后一个OFS=":“替换为OFS=""。

wswtfjt7

wswtfjt75#

这个可以吗?

kent$  echo "2a01:488:66:1000:523:f116:0:1"|awk -F: '{for(i=1;i<=NF;i++)x=x""sprintf ("%4s", $i);gsub(/ /,"0",x);print x}'
2a010488006610000523f11600000001
zqdjd7g9

zqdjd7g96#

POSIX shell解决方案:

#!/bin/sh
expand_ipv6()
{
  __expand_ipv6_ip="${1%%/*}"
  __expand_ipv6_mask=""

  # extract and filter mask at end of address
  case "$1" in
    */*)
      __expand_ipv6_mask="${1#*/}"
      __expand_ipv6_mask="/${__expand_ipv6_mask%%[^0-9/]*}"
  esac

  case "$__expand_ipv6_ip" in
    :*) __expand_ipv6_ip="0$__expand_ipv6_ip"
  esac

  case "$__expand_ipv6_ip" in
    *::*)
      __expand_ipv6_colons="$(echo "$__expand_ipv6_ip" | tr -c -d ':')"
      __expand_ipv6_expanded="$(echo ":::::::::" | sed -e "s/$__expand_ipv6_colons//" -e 's/:/:0/g')"
      __expand_ipv6_ip="$(echo "$__expand_ipv6_ip" | sed "s/::/$__expand_ipv6_expanded/")"
    ;;
  esac

  __expand_ipv6_blocks="$(echo "$__expand_ipv6_ip" | grep -o '[0-9a-f]\+' | while read -r __expand_ipv6_hex; do [ -n "$__expand_ipv6_hex" ] && printf " %d" "$((0x$__expand_ipv6_hex % 65536))"; done)"
  printf "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" $__expand_ipv6_blocks
  printf "%s\n" "$__expand_ipv6_mask"
}

测试:

> expand_ipv6 ab:12:345f::2/32
00ab:0012:345f:0000:0000:0000:0000:0002/32

这是一个基于this answer by user48768的稍微改进的版本,他从这个github source复制了它,其中:

  • 包括对可选掩码的支持(如果输入没有掩码,则也不打印)
  • 是安全的,可以防止代码注入(由于使用了双引号)
  • 不覆盖现有变量(由于__expand_ipv6_-prefix)

注意:expand_ipv6函数需要一个有效的IPv6地址,否则结果也不一定有效。要测试字符串是否是有效的IPv6地址,请用途:

#!/bin/sh
is_ipv6()
{
  ip -6 route get "$1" >/dev/null 2>/dev/null || [ $? -ne 1 ]
}

此外,为了压缩IPv6地址(与expand_ipv6相反),这里还有一个改进的解决方案:

#!/bin/sh
compress_ipv6()
{
  __compress_ipv6_ip="$(echo "$1" | grep -o "[0-9a-f]\+" | while read -r __compress_ipv6_hex; do [ -n "$__compress_ipv6_hex" ] && printf ":%x" "$((0x$__compress_ipv6_hex))"; done)"

  for __compress_ipv6_chain in :0:0:0:0:0:0:0:0 :0:0:0:0:0:0:0 :0:0:0:0:0:0 :0:0:0:0:0 :0:0:0:0 :0:0:0 :0:0 :0
  do
    case "$__compress_ipv6_ip" in
      *$__compress_ipv6_chain*)
        __compress_ipv6_ip="$(echo "$__compress_ipv6_ip" | sed -e "s/$__compress_ipv6_chain/::/" -e 's/:::/::/')"
        break
    esac
  done

  case "$__compress_ipv6_ip" in
    ::*) ;;
    :*) __compress_ipv6_ip="${__compress_ipv6_ip#:}"
  esac
  echo "$__compress_ipv6_ip"
}

相关问题