通过F5实现IPv6地址TOA插入
2020-11-27 13:35:50
熊平
1 关于TOA
1.1 TOA规范草案
tcp header 格式
Option 字段最长 40 字节,每个选项由三部分组成:op-kind、op-length、op-data,常见的 MSS 字段就是在 option 里。IPv4 地址占用 4 个字节,IPv6 占用 16 字节,填充到 option 中没有问题。
(一)IPv4 toa 格式
各字段含义:
- opcode: opcode = 254
- opsize: toa 大小 8 字节
- port: 客户端端口
- clientIP: 客户端 IP(4 字节)
(二)IPv6 toa 格式
各字段含义:
- opcode: opcode = 254
- opsize: toa 大小 20 字节
- port: 客户端端口
- clientIP: 客户端 IP(16 字节)
1.2 服务器端提取TOA信息的模块代码
https://github.com/huaweicloud/elb-toa
2 配置F5 VE
1) 配置tcp profile,允许操作tcp option
2) 配置pool
3) 配置iRules,用于插入toa属性
4) 配置vs,v4和v6,关联iRules和tcp 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.%d} a 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.%d} oct1 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.%d} oct1 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访问IPv4及IPv6的服务,验证抓包和日志。
1) F5上的抓包文件
2) F5日志信息:
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

回复评论
发布评论