The dscp of a GRE packet is not inherited by the IPSEC header

On a RUT955 with configured GRE tunnels and IPsec:

When an IP packet is marked with the dscp “X”, this dscp “X” is inherited in the IP header of the GRE packet. However, when IPsec is configured, this dscp is not transferred to the IP header of the new IP header.
IPsec = transport mode / IKEv2

Does anyone else have this problem or know how to solve it so that IPsec packets retain the DSCP from the GRE packet header?

Thanks.

Hello @Jvendrell,

Linux kernel’s XFRM (IPsec) subsystem does not automatically copy or inherit DSCP/ToS values from the outer IPsec/ESP header down to the inner packet (or vice versa) by default, requiring explicit configuration or firewall mangling.

You can find more about matter using Google search “Linux inheriting dscp from ipsec to gre?”

That will explain three ways around to accomplish that 2-4 in below list when first in which would the desired obviously aren’t there.

  1. use WebUI or uci CLI tool, to do it.
  2. add config directive to that connection that will enable this feature.
  3. use iptables (nftables when RutOS does support it to enable this feature.
  4. use “ip tunnel change gre0 tos inherit” to do that on existing connection.

Lookin WebUI I did not find a way to do it. Searching OpenWRT documents for a while either. Thus assuming that if there was uci method, then that probably search would find something with dscp there but none unfortunately. So first option seems not at this point devices I’ve got available yet latest stable version OS images.

From strongSwan documents that feature can be found and it’s configured under the connection where it’s needed.

That list two last work but both bit hard keep maintaining so that feature stays there all the time even when connection drops, device is booted or even upgraded. Therefore second being best workaround until Teltonika gets to implement it WebUI etc.

I spent few minutes Google Search AI to find a quick way in fairly short conversation how to do this.

After few prompts describing the issue and how I would be worth approaching it, letting it write (saved my effort a bit) the script and explanation then I edited removing flowery style and bold styling etc. Following what came to be.

– cut –
Persistent DSCP Inheritance Patch for strongSwan on Teltonika RUTOS

Summary
This provides a way to persistently enable DSCP inheritance (copy_dscp = yes) within strongSwan (swanctl.conf) on Teltonika RUTOS devices.

Because the RUTOS/OpenWrt Unified Configuration Interface (UCI) overwrites configuration files during boot, interface changes, or WebUI modifications, a standard manual file edit will not survive normal operational cycles. The script intercepts IPsec configuration updates in real-time, injects the necessary parameter, and guarantees persistence across firmware upgrades.

Step 1: Deploy the Hotplug Script
Create a new file as a root user at /etc/hotplug.d/service/99-patch-swanctl to handle automated config injection.

#!/bin/sh
# OpenWrt passes the service name as $1 and the action as $2

if [ "$1" = "ipsec" ] && [ "$2" = "start" ]; then
    # Change if your connecion config is not in this file.
    CONF_FILE="/var/swanctl/swanctl.conf"
    
    # Wait for the RUTOS generation process to complete,
    #  approximate time, adjust a bit if needs it. 
    sleep 2
    
    # Inject configuration if it is missing
    if [ -f "$CONF_FILE" ] && ! grep -q "copy_dscp" "$CONF_FILE"; then
        sed -i '/esp_proposals/i \                copy_dscp = yes' "$CONF_FILE"
        swanctl --load-all >/dev/null 2>&1
    fi
fi

Notice, that strongSwan default config file contents, shown below you see where actual configs are:

less /etc/swanctl/swanctl.conf

include conf.d/*.conf
include /var/run/ipsec/swanctl/swanctl.conf

Step 2: Set file execute permissions
Run following command via SSH to grant execution privileges to the script:

chmod +x /etc/hotplug.d/service/99-patch-swanctl

Step 3: Add script to remain in use also with Firmware Upgrade
Add the script path to /etc/sysupgrade.conf to ensure it is preserved during future RUTOS firmware updates:

cp -av /etc/sysupgrade.conf /etc/sysupgrade.conf.save
echo "/etc/hotplug.d/service/99-patch-swanctl" >> /etc/sysupgrade.conf

Operational Verification

Step 1: Force Configuration Regeneration
Manually trigger the IPsec service init script to simulate a system event and run the hotplug logic:

/etc/init.d/ipsec restart

Step 2: Inspect Runtime Configuration
Verify that the copy_dscp = yes parameter is correctly injected inside the active child configuration block:

grep copy_dscp /var/swanctl/swanctl.conf

and of course also if you like to see whole config

less /var/swanctl/swanctl.conf

– cut –

I do not have a IPSec config at moment (retired old chap with no need for that you know), so I did not test script myself. But if that doesn’t work if you can post a redacted version of your config with approximately similar looking file with that connections { … } section I can fix it so that it will add that option right place.

Anyway above is just a workaround I would in your position do to get things going. Then ask kindly Teltonika to add at this feature to be added as it apparently is very useful with some DMVPN configs which I’ve encountered earlier too. And once they add. move this script from that directory to somewhere else like /etc/config directory, so that you can restart ipsec and verify it’s not needed any more. Then just remove it from /etc/config and also remove entry from /etc/sysupgrade.conf too, which you can do copying saved file top of the modified.

Cheers,
:slight_smile: riku

Hi @mesrik

I understand you.

With your proposal, would each dscp of each IP packet be transferred to the IPsec header? Or will all IPsec headers have the same dscp?

By default, Teltonika doesn’t have anything implemented to solve this problem, correct?

From that strongSwan page see link above, search: copy_dscp and you find following description.

" Since version 5.7.0. Whether to copy the DSCP (Differentiated Services Codepoint) header field to/from the outer IP header in tunnel mode. The value out only copies the field from the inner to the outer header, the value in does the opposite and only copies the field from the outer to the inner header when decapsulating. The value yes copies the field in both directions and the value no disables copying the field altogether. Setting this to yes or in could allow an attacker to adversely affect other traffic at the receiver, which is why the default is out. Controlling this behavior is not supported by all kernel interfaces"

Did that answer your first question?

Second question you read also above cursive why it’s not turned on by default. I’m not right person to comment if Teltonika has implemented something already or not (it could be I didn’t notice to look right place), it would well be that they haven’t had customer(s) this far need for this configuration feature and thus perhaps hasn’t been yet implemented. As you can see there from that list of all options you can set strongSwan config there are plenty of those options. Much more than WebUI shows.

IPsec protocol is old and large protocol suite with lot of options. It is a product of a committee design which all vendors wanted their features built in. Which few common subsets have mostly been used which interoperability is better known. Quite well I understand that even though kernel and ipsec daemon support large number of features for completens reason. Many vendors making products using those rather pick subset what they want to actively support and which they then add simple configuration interfaces. And until someone brings up need something to be added they do not bother trying to implement all bells and whistles increasing product support feature surface. That is IMHO quite understandable business decision. I have been a long time ago position doing for similar reasons. Another angle is also that it’s often easier to add features than take away from product.

ps. Those swanctl.conf paths, possibly are not quite right in that config file, it should be /var/run/ipsec/swanctl/swanctl.conf - where /etc/swanctl/swanctl.conf refers.

Thanks.

But my tunnel is in transport mode. is possible too?

I think you need to find out it yourselves trying it or someone else may answer. Even googling a bit could help you to get answer as I did in the beginning. My point was to get you finding timely alternative workaround in place while you wait Teltonika support tell if and when they perhaps have time to add it.

Hello @Jvendrell,

Apologies for the delayed response.

Could you kindly confirm whether the issue has been resolved, or if you still require any assistance?

Best regards,

Hi,

Teltonika itself is not passing the dscp from the IP header of the GRE packet to the IPsec header.

This is a significant problem in networks with QoS.
Because IPsec packets with dscp 0, when traversing a transport network, will go to the best-effort queues, and the priority generated in the source IP packet will not be lost.

I was looking for an official Teltonika fix for this bug, but it seems there isn’t one.