diff --git a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogFrontend.xml b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogFrontend.xml
index 6fc2948a1..d908c0f01 100644
--- a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogFrontend.xml
+++ b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogFrontend.xml
@@ -208,6 +208,87 @@
checkbox
+
+
+ header
+
+
+ frontend.stickiness_pattern
+
+ dropdown
+ HAProxy documentation for further information.]]>
+ Choose a stick-table type.
+
+
+ frontend.stickiness_dataTypes
+
+ select_multiple
+
+ HAProxy documentation for a full description.]]>
+
+
+ frontend.stickiness_expire
+
+ text
+
+ true
+
+
+ frontend.stickiness_size
+
+ text
+
+ true
+
+
+ frontend.stickiness_length
+
+ text
+
+ true
+
+
+ frontend.stickiness_connRatePeriod
+
+ text
+
+ true
+
+
+ frontend.stickiness_sessRatePeriod
+
+ text
+
+ true
+
+
+ frontend.stickiness_httpReqRatePeriod
+
+ text
+
+ true
+
+
+ frontend.stickiness_httpErrRatePeriod
+
+ text
+
+ true
+
+
+ frontend.stickiness_bytesInRatePeriod
+
+ text
+
+ true
+
+
+ frontend.stickiness_bytesOutRatePeriod
+
+ text
+
+ true
+
header
diff --git a/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml b/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml
index ab97b718c..edba14fbf 100644
--- a/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml
+++ b/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml
@@ -411,6 +411,91 @@
0
Y
+
+ N
+
+ only store IPv4 addresses [default]
+ only store IPv6 addresses
+ store 32bit integers
+ store substrings (max. 32 characters)
+ store binary blocks (max. 32 characters)
+
+
+
+ N
+ Y
+
+ Connection count
+ Current connections
+ Connection rate
+ Session count
+ Session rate
+ HTTP request count
+ HTTP request rate
+ HTTP error count
+ HTTP error rate
+ Bytes in count (client to server)
+ Bytes in rate (client to server)
+ Bytes out count (server to client)
+ Bytes out rate (server to client)
+
+
+
+ Y
+ 30m
+ /^([0-9]{1,5}(?:ms|s|m|h|d)?)/u
+ lower
+ Should be a number between 1 and 5 characters followed by either "d", "h", "m", "s" or "ms".
+
+
+ Y
+ 50k
+ /^([0-9]{1,5}[k|m|g]{1})*/u
+ lower
+ Should be a number between 1 and 5 characters followed by either "k", "m" or "g".
+
+
+ 1
+ 16384
+ Please specify a value between 1 and 16384.
+ N
+
+
+ 10s
+ /^([0-9]{1,8}(?:us|ms|s|m|h|d)?)/u
+ Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".
+ N
+
+
+ 10s
+ /^([0-9]{1,8}(?:us|ms|s|m|h|d)?)/u
+ Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".
+ N
+
+
+ 10s
+ /^([0-9]{1,8}(?:us|ms|s|m|h|d)?)/u
+ Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".
+ N
+
+
+ 10s
+ /^([0-9]{1,8}(?:us|ms|s|m|h|d)?)/u
+ Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".
+ N
+
+
+ 1m
+ /^([0-9]{1,8}(?:us|ms|s|m|h|d)?)/u
+ Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".
+ N
+
+
+ 1m
+ /^([0-9]{1,8}(?:us|ms|s|m|h|d)?)/u
+ Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".
+ N
+
0
Y
diff --git a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf
index cfc150c62..eebeac93e 100644
--- a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf
+++ b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf
@@ -485,6 +485,80 @@
{% endif %}
{%- endmacro %}
+{# Macro expects a backend or frontend object. #}
+{% macro StickTableConfig(proxy, backend=False) -%}
+{% if proxy is defined %}
+{# # check if stickiness is disabled (set to "None") #}
+{% if proxy.stickiness_pattern|default("") != "" %}
+ # stickiness
+{# # check if additional data types are configured #}
+{% if proxy.stickiness_dataTypes|default("") != "" %}
+{% set stickiness_datatypes = [] %}
+{% for datatype in proxy.stickiness_dataTypes.split(",") %}
+{# # add time period to all types where this is required #}
+{% if datatype == 'conn_rate' %}
+{% do stickiness_datatypes.append(datatype ~ '(' ~ proxy.stickiness_connRatePeriod ~ ')') %}
+{% elif datatype == 'sess_rate' %}
+{% do stickiness_datatypes.append(datatype ~ '(' ~ proxy.stickiness_sessRatePeriod ~ ')') %}
+{% elif datatype == 'http_req_rate' %}
+{% do stickiness_datatypes.append(datatype ~ '(' ~ proxy.stickiness_httpReqRatePeriod ~ ')') %}
+{% elif datatype == 'http_err_rate' %}
+{% do stickiness_datatypes.append(datatype ~ '(' ~ proxy.stickiness_httpErrRatePeriod ~ ')') %}
+{% elif datatype == 'bytes_in_rate' %}
+{% do stickiness_datatypes.append(datatype ~ '(' ~ proxy.stickiness_bytesInRatePeriod ~ ')') %}
+{% elif datatype == 'bytes_out_rate' %}
+{% do stickiness_datatypes.append(datatype ~ '(' ~ proxy.stickiness_bytesOutRatePeriod ~ ')') %}
+{% else %}
+{% do stickiness_datatypes.append(datatype) %}
+{% endif %}
+{% endfor %}
+{% set stickiness_store = 'store ' ~ stickiness_datatypes|join(',') %}
+{% endif %}
+{# # check stick-table type #}
+{% if proxy.stickiness_pattern == "sourceipv4" or proxy.stickiness_pattern == "ipv4" %}
+{% set table_type = 'ip' %}
+{% elif proxy.stickiness_pattern == "sourceipv6" or proxy.stickiness_pattern == "ipv6" %}
+{% set table_type = 'ipv6' %}
+{% elif proxy.stickiness_pattern == "cookievalue" or proxy.stickiness_pattern == "string" %}
+{% set table_type = 'string' %}
+{% set add_length = True %}
+{% elif proxy.stickiness_pattern == "rdpcookie" or proxy.stickiness_pattern == "binary" %}
+{% set table_type = 'binary' %}
+{% set add_length = True %}
+{% elif proxy.stickiness_pattern == "integer" %}
+{% set table_type = 'integer' %}
+{% endif %}
+{# # check data length #}
+{% if add_length is defined %}
+{% if proxy.stickiness_cookielength is defined %}
+{% set data_length = proxy.stickiness_cookielength %}
+{% elif proxy.stickiness_length is defined %}
+{% set data_length = proxy.stickiness_length %}
+{% else %}
+{% set data_length = '32' %}
+{% endif %}
+{% endif %}
+{# # add stick-table #}
+{% if table_type is defined %}
+ stick-table type {{table_type}} {%if add_length is defined %}len {{data_length}} {% endif %}size {{proxy.stickiness_size}} expire {{proxy.stickiness_expire}} {{stickiness_store}}
+{% endif %}
+{# # stick-table persistence (backends only) #}
+{%- if backend == True -%}
+{%- if proxy.stickiness_pattern == "cookievalue" %}
+ stick store-response res.cook({{proxy.stickiness_cookiename}})
+ stick on req.cook({{proxy.stickiness_cookiename}})
+{%- elif proxy.stickiness_pattern == "rdpcookie" %}
+ stick on req.rdp_cookie(mstshash)
+{%- elif proxy.stickiness_pattern != '' %}
+ stick on src
+{%- endif -%}
+{%- endif -%}
+{% endif %}
+{% else %}
+# ERROR: StickTableConfig called with empty data
+{% endif %}
+{%- endmacro -%}
+
{% if not (helpers.exists('OPNsense.HAProxy.general') and OPNsense.HAProxy.general.enabled|default("0") == "1") %}
#
# NOTE: HAProxy is currently DISABLED
@@ -688,6 +762,8 @@ frontend {{frontend.name}}
{% if frontend.tuning_timeoutHttpKeepAlive|default("") != "" and frontend.mode == 'http' %}
timeout http-keep-alive {{frontend.tuning_timeoutHttpKeepAlive}}
{% endif %}
+{# # call macro to evaluate stickiness config #}
+{{ StickTableConfig(frontend) }}
# logging options
{% if frontend.logging_dontLogNull=='1' %}
option dontlognull
@@ -846,48 +922,8 @@ backend {{backend.name}}
{# # (redundant) GUI option for this. #}
mode {{backend.mode}}
balance {{backend.algorithm}}
-{# # check if stickiness is disabled (set to "None") #}
-{% if backend.stickiness_pattern|default("") != "" %}
- # stickiness
-{# # check if additional data types are configured #}
-{% if backend.stickiness_dataTypes|default("") != "" %}
-{% set stickiness_datatypes = [] %}
-{% for datatype in backend.stickiness_dataTypes.split(",") %}
-{# # add time period to all types where this is required #}
-{% if datatype == 'conn_rate' %}
-{% do stickiness_datatypes.append(datatype ~ '(' ~ backend.stickiness_connRatePeriod ~ ')') %}
-{% elif datatype == 'sess_rate' %}
-{% do stickiness_datatypes.append(datatype ~ '(' ~ backend.stickiness_sessRatePeriod ~ ')') %}
-{% elif datatype == 'http_req_rate' %}
-{% do stickiness_datatypes.append(datatype ~ '(' ~ backend.stickiness_httpReqRatePeriod ~ ')') %}
-{% elif datatype == 'http_err_rate' %}
-{% do stickiness_datatypes.append(datatype ~ '(' ~ backend.stickiness_httpErrRatePeriod ~ ')') %}
-{% elif datatype == 'bytes_in_rate' %}
-{% do stickiness_datatypes.append(datatype ~ '(' ~ backend.stickiness_bytesInRatePeriod ~ ')') %}
-{% elif datatype == 'bytes_out_rate' %}
-{% do stickiness_datatypes.append(datatype ~ '(' ~ backend.stickiness_bytesOutRatePeriod ~ ')') %}
-{% else %}
-{% do stickiness_datatypes.append(datatype) %}
-{% endif %}
-{% endfor %}
-{% set stickiness_store = 'store ' ~ stickiness_datatypes|join(',') %}
-{% endif %}
-{# # check stick-table type #}
-{% if backend.stickiness_pattern == "sourceipv4" %}
- stick-table type ip size {{backend.stickiness_size}} expire {{backend.stickiness_expire}} {{stickiness_store}}
- stick on src
-{% elif backend.stickiness_pattern == "sourceipv6" %}
- stick-table type ipv6 size {{backend.stickiness_size}} expire {{backend.stickiness_expire}} {{stickiness_store}}
- stick on src
-{% elif backend.stickiness_pattern == "cookievalue" %}
- stick-table type string len {{backend.stickiness_cookielength}} size {{backend.stickiness_size}} expire {{backend.stickiness_expire}} {{stickiness_store}}
- stick store-response res.cook({{backend.stickiness_cookiename}})
- stick on req.cook({{backend.stickiness_cookiename}})
-{% elif backend.stickiness_pattern == "rdpcookie" %}
- stick-table type binary len {{backend.stickiness_cookielength}} size {{backend.stickiness_size}} expire {{backend.stickiness_expire}} {{stickiness_store}}
- stick on req.rdp_cookie(mstshash)
-{% endif %}
-{% endif %}
+{# # call macro to evaluate stickiness config #}
+{{ StickTableConfig(backend,true) }}
# tuning options
{% if backend.tuning_timeoutConnect|default("") != "" %}
timeout connect {{backend.tuning_timeoutConnect}}