# Proxy Protocol v2 Initiator

**URL:** <https://community.f5.com/t/proxy-protocol-v2-initiator/74827>\
**Category:** Community CodeShare\
**Tags:** big-ip, irules, proxy, ltm\
**Created:** [July 17, 2024, 1:49am UTC](https://community.f5.com/t/proxy-protocol-v2-initiator/74827 "2024-07-17T01:49:22Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Won-Soo](https://d1p9zq3aats0t8.cloudfront.net/user_avatar/community.f5.com/won-soo/32/7407_2.png) [@Won-Soo](https://community.f5.com/u/Won-Soo)\
**Post date:** [July 17, 2024, 1:49am UTC](https://community.f5.com/t/proxy-protocol-v2-initiator/74827/1 "2024-07-17T01:49:22Z")

</div>

**Problem this snippet solves:**

Proxy Protocol v1 related articles have already been posted on DevCentral, but there is no v2 support iRule code available. A customer wanted to support Proxy Protocol v2, so I wrote an iRule code for supporting v2.

- [Proxy protocol for the BIG-IP (f5.com)](https://my.f5.com/manage/s/article/K40512493)

**How to use this snippet:**

Back-end server must handle Proxy header prior data exchange.

**Code :**

```tcl
when CLIENT_ACCEPTED {
	# DEBUG On/Off 
	set DEBUG 0

	set v2_proxy_header "0d0a0d0a000d0a515549540a"
	
	# v2 version and command : 0x21 - version 2 & PROXY command
	set v2_ver_command "21"

	# v2 address family and transport protocol : 0x11 - AF_INET (IPv4) & TCP protocol
	set v2_af_tp "11"

	# v2 Address Size : 0x000C - 12 bytes for IPv4 + TCP 
	set v2_address_length "000c"

	# Get TCP port - 2 byte hexadecimal format
	set src_port [format "%04x" [TCP::client_port]]
	set dst_port [format "%04x" [TCP::local_port]]

	# Get Src Address and convert to 4 byte hexadecimal format
	foreach val [split [IP::client_addr] "."] {
		append src_addr [format "%02x" $val]
	}

	# Get Dst Address and convert to 4 byte hexadecimal format
	foreach val [split [IP::local_addr] "."] {
		append dst_addr [format "%02x" $val]
	}

	# Build proxy v2 data
	set proxy_data [binary format H* "${v2_proxy_header}${v2_ver_command}${v2_af_tp}${v2_address_length}${src_addr}${dst_addr}${src_port}${dst_port}"]

	if { $DEBUG } {
		binary scan $proxy_data H* proxy_dump
		log local0. "[IP::client_addr]:[TCP::client_port]_[IP::local_addr]:[TCP::local_port] - proxy_data dump : $proxy_dump"
	}
}

when SERVER_CONNECTED {
	TCP::respond $proxy_data
}

```

---

<div class="post-metadata">

**Author:** ![RdJ](https://avatars.discourse-cdn.com/v4/letter/r/ad7895/32.png) [@RdJ](https://community.f5.com/u/RdJ)\
**Post date:** [March 28, 2025, 7:40am UTC](https://community.f5.com/t/proxy-protocol-v2-initiator/74827/2 "2025-03-28T07:40:57Z")

</div>

Thanks, I’ll test this soon. Could you create a variant for IPv6 as well?

---

<div class="post-metadata">

**Author:** ![aobrien5](https://avatars.discourse-cdn.com/v4/letter/a/e56c9b/32.png) [@aobrien5](https://community.f5.com/u/aobrien5)\
**Post date:** [January 8, 2026, 2:41pm UTC](https://community.f5.com/t/proxy-protocol-v2-initiator/74827/3 "2026-01-08T14:41:54Z")

</div>

I added IPv6 support like this. I can see room for a bit of improvement, but it works well.

```tcl
when CLIENT_ACCEPTED {
    # DEBUG On/Off
    set DEBUG 1

    # v2 proxy header
    set v2_proxy_header "0d0a0d0a000d0a515549540a"

    # v2 version and command: 0x21 (version 2 & PROXY command)
    set v2_ver_command "21"

    # Get ports
    set src_port [format "%04x" [TCP::client_port]]
    set dst_port [format "%04x" [TCP::local_port]]

    # Detect IP version
    if { [IP::version] == 4 } {
        # IPv4
        set v2_af_tp "11"
        set v2_address_length "000c"

        # Convert IPv4 addresses to hex
        set src_addr ""
        foreach val [split [IP::client_addr] "."] {
            append src_addr [format "%02x" $val]
        }
        set dst_addr ""
        foreach val [split [IP::local_addr] "."] {
            append dst_addr [format "%02x" $val]
        }

    } elseif { [IP::version] == 6 } {
        # IPv6
        set v2_af_tp "21"
        set v2_address_length "0024"

        # Convert IPv6 addresses to 16-byte hex
        set src_addr ""
        set exp_src_addr [split [IP::client_addr] ":"]

        foreach block $exp_src_addr {
            # If block is null, then v6 address is compressed with ::
            if {$block ne ""} {
                append src_addr [format "%04x" [scan $block %x]]    
            } else {
                # Insert 0000 for each block compressed out of the address
                for {set x [llength $exp_src_addr]} { $x <= 8 } { incr x } {
                    append src_addr "0000"
                }
            }
        }

        set dst_addr ""
        set exp_dst_addr [split [IP::local_addr] ":"]

        foreach block $exp_dst_addr {
            # If block is null, then v6 address is compressed with ::
            if {$block ne ""} {
                append dst_addr [format "%04x" [scan $block %x]]    
            } else {
                # Insert 0000 for each block compressed out of the address
                for {set x [llength $exp_dst_addr]} { $x <= 8 } { incr x } {
                    append dst_addr "0000"
                }
            }
        }
    } else {
        set v2_af_tp ""
        set v2_address_length ""
        set src_addr ""
        set dst_addr ""
    }

    # Build proxy v2 data
    set proxy_data [binary format H* "${v2_proxy_header}${v2_ver_command}${v2_af_tp}${v2_address_length}${src_addr}${dst_addr}${src_port}${dst_port}"]

    if { $DEBUG } {
        binary scan $proxy_data H* proxy_dump
        log local0. "[IP::client_addr]:[TCP::client_port]_[IP::local_addr]:[TCP::local_port] - proxy_data dump : $proxy_dump"
    }
}

when SERVER_CONNECTED {
	TCP::respond $proxy_data
}

```
