F5社区-F5技术交流中心

通过F5实现IPv6地址TOA插入

2020-11-27 13:35:50

熊平

1     关于TOA

1.1   TOA规范草案

tcp header 格式


Option 字段最长 40 字节,每个选项由三部分组成:op-kindop-lengthop-data,常见的 MSS 字段就是在 option 里。IPv4 地址占用 4 个字节,IPv6 占用 16 字节,填充到 option 中没有问题。

(一)IPv4 toa 格式

各字段含义:

  • opcode: opcode = 254
  • opsize: toa 大小 8 字节
  • port: 客户端端口
  • clientIP: 客户端 IP字节)

(二)IPv6 toa 格式

各字段含义:

  • opcode: opcode = 254
  • opsize: toa 大小 20 字节
  • port: 客户端端口
  • clientIP: 客户端 IP16 字节)

 

1.2  服务器端提取TOA信息的模块代码

https://github.com/huaweicloud/elb-toa



2     配置F5 VE

1)      配置tcp profile,允许操作tcp option

2)      配置pool

3)      配置iRules,用于插入toa属性

4)      配置vsv4v6,关联iRulestcp profile

 

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# list ltm profile tcp tcp_toa 

ltm profile tcp tcp_toa {

    app-service none

    tcp-options "{254 first}"

}

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# 

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# 

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# 

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# list ltm pool

ltm pool pool_http {

    members {

        10.3.0.8:http {

            address 10.3.0.8

            session monitor-enabled

            state up

        }

    }

    monitor tcp_half_open

}

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)#

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# list ltm virtual 

ltm virtual va_http_v4 {

    creation-time 2020-11-01:10:44:59

    destination 10.3.0.7:amanda

    ip-protocol tcp

    last-modified-time 2020-11-01:10:55:23

    mask 255.255.255.255

    pool pool_http

    profiles {

        tcp_toa { }

    }

    rules {

        toa_v4

    }

    serverssl-use-sni disabled

    source 0.0.0.0/0

    source-address-translation {

        type automap

    }

    translate-address enabled

    translate-port enabled

    vs-index 2

}

ltm virtual vs_http_v6 {

    creation-time 2020-11-01:10:45:45

    destination ace:cab:deca:deed::5.amanda

    ip-protocol tcp

    last-modified-time 2020-11-01:10:55:37

    pool pool_http

    profiles {

        tcp_toa { }

    }

    rules {

        toa_v6

    }

    serverssl-use-sni disabled

    source-address-translation {

        type automap

    }

    translate-address enabled

    translate-port enabled

    vs-index 3

}

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# 

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# list ltm rule toa

Configuration Items:

  toa_v4   toa_v6   

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# list ltm rule toa_v4

ltm rule toa_v4 {

#For v14+, you can insert option in initial SYN

 

when SERVER_INIT {

    log local0. "The IP address:port was [IP::client_addr]:[TCP::client_port]"

    scan [IP::client_addr] {%d.%d.%d.%da b c d

    log local0. "Client port in hex: [format %x [TCP::client_port]]"

    TCP::option set 254 [binary format H4cccc [format %x [TCP::client_port]] $a $b $c $d] all

}

 

}

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)# list ltm rule toa_v6

ltm rule toa_v6 {

#https://devcentral.f5.com/s/articles/tcl-procedures-to-compress-expand-a-ipv6-address-notation-988

when RULE_INIT {

    # Initialize the array used to expand compressed IPv6 groups to 16 bit

    array set static::ipv6_grp_filler {

        "1" "000"

        "2" "00"

        "3" "0"

        "4" ""

    }

    # Initialize the array used to expand compressed IPv6 addresses to 128 bit

    array set static::ipv6_addr_filler {

        "0"  "0000:0000:0000:0000:0000:0000:0000:0000"

        "5"  "0000:0000:0000:0000:0000:0000:0000"

        "10" "0000:0000:0000:0000:0000:0000"

        "15" "0000:0000:0000:0000:0000"

        "20" "0000:0000:0000:0000"

        "25" "0000:0000:0000"

        "30" "0000:0000"

        "35" "0000"

        "40" ""

    }

    # Initialize the array used to perform a IPv4 (decimal 0-255) to IPv6 (hex 00-FF) conversation.

    for { set i 0 } { $i <= 255 } { incr i } {

        set static::ipv6_dec_map($i) [format %02x $i]

    }

 

    #

    # Example procedure calls (samples can be removed)

    #

 

    set input "2001:0001:0022:0333:4444:0:0:0:1%1"

    set output [call compress_ipv6_addr $input]

    log local0.debug "Input: $input Output: $output"

 

    set input "2001:ef:123::192.168.1.1%2"

    set output [call expand_ipv6_addr $input]

    log local0.debug "Input: $input Output: $output"

 

}

proc compress_ipv6_addr { addr } {

    # Enumerate and store IPv6 ZoneID / Route Domain suffix

    if { [set id [getfield $addr "%" 2]] ne "" } then {

        set id "%$id"

        set addr [getfield $addr "%" 1]

    }

    # X encode (e.g. :0001 becomes :X1) leading zeros on the individual IPv6 address groups (left orientated searches)

    set addr [string map [list ":0000" ":X"   ":000" ":X"   ":00" ":X"   ":0" ":X"   "|0000" "X"   "|000" "X"   "|00" "X"   "|0" "X" ] "|$addr|"]

    # Restoring the required X encoded zeros (e.g. :X: becomes :0:) while removing any other X encodings and | separators (right orientated searches)

    set addr [string map [list "X:" "0:"   "X|" "0"   "X." "0."   "X" ""   "|" "" $addr]

    # Find the longest range of consecutive zero value IPv6 address groups and then replace the most significant groups with the :: notation.

    switch -glob -- $addr {

        "*::*"            #Already compressed }

        "0:0:0:0:0:0:0:0" { set addr "::}

        "0:0:0:0:0:0:0:*" { set addr ":[string range $addr 13 end]}

        "*:0:0:0:0:0:0:0" { set addr "[string range $addr 0 end-13]:" }

        "0:0:0:0:0:0:*"   set addr ":[string range $addr 11 end]}

        "*:0:0:0:0:0:0:*" { set addr "[substr $addr 0 ":"]::[findstr $addr ":0:0:0:0:0:0:" 13]}

        "*:0:0:0:0:0:0"   set addr "[string range $addr 0 end-11]:" }

        "0:0:0:0:0:*"     set addr ":[string range $addr 9 end]}

        "*:0:0:0:0:0:*"   set addr "[substr $addr 0 ":0:"]::[findstr $addr ":0:0:0:0:0:" 11]}

        "*:0:0:0:0:0"     set addr "[string range $addr 0 end-9]:" }

        "0:0:0:0:*"       set addr ":[string range $addr 7 end]}

        "*:0:0:0:0:*"     set addr "[substr $addr 0 ":0:0:"]::[findstr $addr ":0:0:0:0:" 9]}

        "*:0:0:0:0"       set addr "[string range $addr 0 end-7]:" }

        "0:0:0:*"         set addr ":[string range $addr 5 end]}

        "*:0:0:0:*"       set addr "[substr $addr 0 ":0:0:0:"]::[findstr $addr ":0:0:0:" 7]}

        "*:0:0:0"         set addr "[string range $addr 0 end-5]:" }

        "0:0:*"           set addr ":[string range $addr 3 end]}

        "*:0:0:*"         set addr "[substr $addr 0 ":0:0:"]::[findstr $addr ":0:0:" 5]}

        "*:0:0"           set addr "[string range $addr 0 end-3]:" }

    }

    # Append the previously extracted IPv6 ZoneID / Route Domain suffix and return the compressed IPv6 address

    return "$addr$id"

}

 

 

 

proc expand_ipv6_addr { addr } {

    if { [catch {

        # Enumerating and storing IPv6 ZoneID / Route Domain suffix

        if { [set id [getfield $addr "%" 2]] ne "" } then {

            set id "%$id"

            set addr [getfield $addr "%" 1]

        }

        # Parsing the first IPv6 address block of a possible :: notation by splitting the block into : separated IPv6 address groups

        set blk1 ""

        foreach grp [split [getfield $addr "::" 1] ":"] {

            # Check if current group contains a IPv4 address notation

            if { $grp contains "." } then {

                # The current group contains a IPv4 address notation. Trying to extract the four IPv4 address octets

                scan $grp {%d.%d.%d.%doct1 oct2 oct3 oct4

                # Convert the four IPv4 address octets into two IPv6 address groups by querying the $static::ipv6_dec_map array

                append blk1 "$static::ipv6_dec_map($oct1)$static::ipv6_dec_map($oct2$static::ipv6_dec_map($oct3)$static::ipv6_dec_map($oct4) "

                set oct4 ""

            } else {

                # The current group contains just a IPv6 address notation. Filling up the IPv6 address group with leading zeros by querying the $static::ipv6_grp_filler array

                append blk1 "$static::ipv6_grp_filler([string length $grp])$grp "

            }

        }

        # Parsing the second IPv6 address block of a possible :: notation by splitting the block into : IPv6 address separated groups

        set blk2 ""

        foreach grp [split [getfield $addr "::" 2] ":"] {

            # Check if current group contains a IPv4 address notation

            if { $grp contains "." } then {

                # The current group contains a IPv4 address notation. Trying to extract the four IPv4 address octets

                scan $grp {%d.%d.%d.%doct1 oct2 oct3 oct4

                # Convert the four IPv4 address octets into two IPv6 address groups by querying the $static::ipv6_dec_map array

                append blk2 "$static::ipv6_dec_map($oct1)$static::ipv6_dec_map($oct2$static::ipv6_dec_map($oct3)$static::ipv6_dec_map($oct4) "

                set oct4 ""

            } else {

                # The current group contains just a IPv6 address notation. Filling up the IPv6 address group with leading zeros by querying the $static::ipv6_grp_filler array

                append blk2 "$static::ipv6_grp_filler([string length $grp])$grp "

            }

        }

        # Joining the first and second block of the possible :: notation while expanding the address to 128bit length by querying the $static::ipv6_addr_filler array

        set addr "[join "$blk1$static::ipv6_addr_filler([string length "$blk1$blk2"]) $blk2" ":"]"

    }] } then {

        # log local0.debug "errorInfo: [subst \$::errorInfo]"

        # return "errorInfo: [subst \$::errorInfo]"

        return ""

    }

    # Append the previously extracted IPv6 ZoneID / Route Domain suffix and return the expanded IPv6 address notation

    #return "$addr$id"

    return $addr

}

 

#For v14+, you can insert option in initial SYN

 

when SERVER_INIT {

    log local0. "The IP address was [IP::client_addr]"

    set ip [IP::client_addr]

    set big6 [call expand_ipv6_addr $ip]

    set nosep [string map {: ""$big6]

    log local0. "ClientIP: $ip big6: $big6 nosep: $nosep"

    log local0. "Client port in hex: [format %x [TCP::client_port]]"

    TCP::option set 254 [binary format H* [format %x [TCP::client_port]]$nosep] all

}

 

}

azureuser@(localhost)(cfg-sync Standalone)(Active)(/Common)(tmos)#

 

3     验证测试

使用浏览器或curl访问IPv4IPv6的服务,验证抓包和日志。

1)      F5上的抓包文件



2)      F5日志信息:

Timestamp

Log Level

Host

Service

Status Code

Event

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm1[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: Client port in hex: ef7b

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm1[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: ClientIP: 240e:46d:8281:2dab:b534:d0af:5f6d:934e big6: 240e:046d:8281:2dab:b534:d0af:5f6d:934e nosep: 240e046d82812dabb534d0af5f6d934e

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm1[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: The IP address:port was 240e:46d:8281:2dab:b534:d0af:5f6d:934e:61307

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm2[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: Client port in hex: f0df

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm2[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: ClientIP: 240e:46d:8281:2dab:b534:d0af:5f6d:934e big6: 240e:046d:8281:2dab:b534:d0af:5f6d:934e nosep: 240e046d82812dabb534d0af5f6d934e

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm2[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: The IP address:port was 240e:46d:8281:2dab:b534:d0af:5f6d:934e:61663

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm1[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: Client port in hex: f0de

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm1[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: ClientIP: 240e:46d:8281:2dab:b534:d0af:5f6d:934e big6: 240e:046d:8281:2dab:b534:d0af:5f6d:934e nosep: 240e046d82812dabb534d0af5f6d934e

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm1[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: The IP address:port was 240e:46d:8281:2dab:b534:d0af:5f6d:934e:61662

Sun Nov 1 22:47:03 CST 2020

info

bigip1

tmm[11381]

 

Rule /Common/toa_v6 <SERVER_CONNECTED>: Client port in hex: f0dd

 


服务器端的验证信息:




服务器端能够收到并解析客户端IPv6地址及端口。

发布评论 加入社群

发布评论

Will Tang 2020-11-27 15:46:32 0

干货,感谢熊老师!

王亚军 2020-12-03 14:09:48 0

赞赞赞

相关文章

博文精选|工行“去 O”数据库选型与分布式架构设计

F5小安

2021-09-17 10:45:32 904

F5支持Proxy Protocol测试

邹善

2021-06-20 01:09:23 2166

通过F5提取TOA中的真实源地址

Will Tang

2020-11-27 17:22:57 4004

Login

手机号
验证码
© 2019 F5 Networks, Inc. 版权所有。京ICP备16013763号-1