Strongswan VPN multicast and packet mark problem

Hello everybody,
I have a working policy-based VPN set up with Strongswan and I wanted to add multicast forwarding to it by means of the forecast plugin. The problem is that when I set mark=%unique in ipsec.conf multicast traffic gets correctly forwarded and I can access my LAN, but WAN connection is not working anymore (e.g. I cannot ping 8.8.8.8 from the VPN client).
It's the same problem as in this thread: https://wiki.strongswan.org/issues/3392 (I hope there's no problem with posting the link).
As I understand the issue is that after the traffic to the external network is SNATed (rather than masqueraded because I have a static IP), the reply comes in with my public IP address rather than the VPN address and the iptables rules installed by the forecast plugin do not work.
I have tried to set these rules:
iptables -t mangle -I FORWARD ! -d 10.10.10.0/24 -m policy --dir in --pol ipsec --proto esp -j CONNMARK --save-mark
iptables -t mangle -I PREROUTING -i vlan_ptm0 -j CONNMARK --restore-mark
as suggested in the link (accordingly modified to my configuration) but they don't seem to help.

ipsec.conf
# ipsec.conf - strongSwan IPsec configuration file

config setup
    uniqueids=yes
    charondebug="cfg 2, dmn 2, ike 2, net 2, knl 2"

conn "*** VPN"
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=no
    leftfirewall=yes
    mobike=yes
    dpdaction=clear
    dpddelay=300s
    rekey=no
    left=%any
    leftid=dns:Server.***.VPN
    leftsendcert=always
    leftauth=pubkey
    leftsubnet=0.0.0.0/0,::/0
    right=%any
    rightauth=eap-tls
    eap_identity=%any
    rightsourceip=10.10.10.0/24,****:****:****:****::/64
    rightdns=192.168.1.254,fd69:beef:cafe:ca5a::1
    rightsendcert=yes
    ike=aes256-sha256-modp2048!
    esp=aes256-sha256!
    mark=%unique
/etc/config/firewall
config defaults
	option syn_flood '1'
	option input 'DROP'
	option output 'ACCEPT'
	option forward 'REJECT'
	option drop_invalid '1'

config zone 'lan'
	option name 'lan'
	list network 'lan'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	option mtu_fix '1'
	option wan '0'

config zone 'wan'
	option name 'wan'
	list network 'wan'
	list network 'route48'
	option input 'DROP'
	option output 'ACCEPT'
	option forward 'DROP'
	option masq '1'
	list masq_src 'lan'
	option mtu_fix '1'
	option wan '1'

config forwarding 'lan_wan'
	option src 'lan'
	option dest 'wan'

config rule 'Allow_DHCP_Renew_wan'
	option name 'Allow-DHCP-Renew-wan'
	option src 'wan'
	option proto 'udp'
	option dest_port '68'
	option target 'ACCEPT'
	option family 'ipv4'

config rule 'Allow_Ping_wan'
	option name 'Allow-Ping-wan'
	option src 'wan'
	option proto 'icmp'
	list icmp_type 'echo-request'
	option limit '1000/sec'
	option family 'ipv4'
	option target 'ACCEPT'

config rule 'Allow_ICMPv6_Input'
	option name 'Allow-ICMPv6-Input'
	option src 'wan'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	list icmp_type 'router-solicitation'
	list icmp_type 'neighbour-solicitation'
	list icmp_type 'router-advertisement'
	list icmp_type 'neighbour-advertisement'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule 'Allow_ICMPv6_Forward'
	option name 'Allow-ICMPv6-Forward'
	option src 'wan'
	option dest '*'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config include
	option path '/etc/firewall.user'
	option reload '1'

config defaultrule 'defaultoutgoing'
	option name 'Default action for outgoing NAT'
	option src 'lan'
	option dest 'wan'
	option proto 'all'
	option target 'ACCEPT'

config rule 'SSH_wan'
	option src 'wan'
	option name 'SSH_wan'
	option target 'DROP'
	option proto 'tcp'
	option dest_port '22'
	option family 'ipv4'

config rule
	option name 'IPSec-ESP'
	option src 'wan'
	option proto 'esp'
	option target 'ACCEPT'

config rule
	option name 'IPSec-IKE'
	option src 'wan'
	option dest_port '500'
	option proto 'udp'
	option target 'ACCEPT'

config rule
	option name 'IPSec-NAT-T'
	option src 'wan'
	option dest_port '4500'
	option proto 'udp'
	option target 'ACCEPT'

config rule
	option name 'IPSec-Auth-Header'
	option src 'wan'
	option proto 'ah'
	option target 'ACCEPT'
/etc/firewall.user
# Strongswan IPSec VPN
iptables -I INPUT   -m policy --dir in  --pol ipsec --proto esp -j ACCEPT
iptables -I FORWARD -m policy --dir in  --pol ipsec --proto esp -j ACCEPT
iptables -I FORWARD -m policy --dir out --pol ipsec --proto esp -j ACCEPT
iptables -I OUTPUT  -m policy --dir out --pol ipsec --proto esp -j ACCEPT
ip6tables -I INPUT   -m policy --dir in  --pol ipsec --proto esp -j ACCEPT
ip6tables -I FORWARD -m policy --dir in  --pol ipsec --proto esp -j ACCEPT
ip6tables -I FORWARD -m policy --dir out --pol ipsec --proto esp -j ACCEPT
ip6tables -I OUTPUT  -m policy --dir out --pol ipsec --proto esp -j ACCEPT
iptables -t nat -I POSTROUTING -s 10.10.10.0/24 -o vlan_ptm0 -m policy --dir out --pol ipsec -j ACCEPT
iptables -t nat -I POSTROUTING -s 10.10.10.0/24 -o vlan_ptm0 -j SNAT --to ***ROUTER_WAN_IP***
iptables -t nat -I POSTROUTING -m policy --pol ipsec --dir out -j ACCEPT

# Fix me for multicast over VPN
# iptables -t mangle -I FORWARD ! -d 10.10.10.0/24 -m policy --dir in --pol ipsec --proto esp -j CONNMARK --save-mark
# iptables -t mangle -I PREROUTING -i vlan_ptm0 -j CONNMARK --restore-mark
iptables -t mangle --list (VPN client connected)
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
MARK       all  --  anywhere             10.10.10.1           MARK set 0x1
MARK       udp  --  ***CLIENT_WAN_IP***  ***ROUTER_WAN_IP***  udp spt:63591 dpt:4500 MARK set 0x1

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
TCPMSS     tcp  --  anywhere             anywhere             tcp flags:SYN,RST/SYN /* !fw3: lan (mtu_fix) */ TCPMSS clamp to PMTU
TCPMSS     tcp  --  anywhere             anywhere             tcp flags:SYN,RST/SYN /* !fw3: wan (mtu_fix) */ TCPMSS clamp to PMTU

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
MARK       all  --  anywhere             10.10.10.1           MARK set 0x1

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

Does anybody have any idea?

Does it work if you manually set the mark? For instance,

iptables -t mangle -I PREROUTING -i vlan_ptm0 -j MARK --set-mark 1

(The actual mark value has to match the existing rule and obviously will only make it work for one client. Also, instead of -i vlan_ptm0 you could try -d **ROUTER_WAN_IP**.)

1 Like

Ok, it worked by manually setting the mark as you suggested. Then I tried to dig further with iptables trace and I discovered all the packets entering from vlan_ptm0 have the mark 0x80000000. The problem is that the rule installed by Strongswan appears to add the mark, rather than overwriting it.
I added the following rules:

Summary
iptables -t mangle -I PREROUTING -s 10.10.10.0/24 -m mark ! --mark 0x8000000 -j MARK --set-xmark 0/0x8000000
iptables -t mangle -I FORWARD ! -d 10.10.10.0/24 -m policy --dir in --pol ipsec --proto esp -j CONNMARK --save-mark
iptables -t mangle -I INPUT -m policy --dir in --pol ipsec --proto esp -j CONNMARK --save-mark
iptables -t mangle -I PREROUTING -i vlan_ptm0 -m mark --mark 0x8000000 -j CONNMARK --restore-mark
ip6tables -t mangle -I PREROUTING -s VPN_IPv6_SUBNET -m mark ! --mark 0x8000000 -j MARK --set-xmark 0/0x8000000
ip6tables -t mangle -I FORWARD ! -d VPN_IPv6_SUBNET -m policy --dir in --pol ipsec --proto esp -j CONNMARK --save-mark
ip6tables -t mangle -I INPUT -m policy --dir in --pol ipsec --proto esp -j CONNMARK --save-mark
ip6tables -t mangle -I PREROUTING -i 6in4-route48 -j CONNMARK --restore-mark

and now it works! So thank you for your suggestion!
Basically --set-xmark 0/0x8000000 subtracts the already present mark (bit to bit AND with the negation of the mark).
Now I am left with only one problem: connections to the router itself (e.g. luci or SSH) from a VPN client do not work: reply packets from the router are not delivered to the client. This works without setting mark=%unique in ipsec.conf.
The strange thing is I tried tcpdump -i any -nl port 9443 on the router (9443 is my luci port) and no reply is captured (same for SSH). Replies are captured instead if I visit the webpage from a LAN client.
Even more strange is that no reply is captured without mark=%unique and the website actually loading correctly on the client.
My hypothesis is that this kind of packets do not go through the firewall and therefore they're not marked, so the connection doesn't work when mark=%unique is set, but shouldn't the replies be shown by tcpdump anyway? I don't understand. :thinking:

Hm, not sure what you mean exactly, because each packet can only have one mark (I guess working with masks can give you separate values, but the rules installed by the forecast plugin explicitly set the mark with mask 0xffffffff, so they overwrite it completely). Also, strongSwan won't use values that high if you use mark=%unique, so that 0x08000000 value definitely comes from somewhere else. Do you see the rule that sets it, e.g. in iptables-save?

So is the problem that the mark that's saved via CONNMARK target for inbound traffic is e.g. 0x08000001? And when it's restored this won't match the outbound IPsec policy (which has a mark of 0x00000001)?

Theoretically, that bit in the mark could be ignored by setting mark=%unique/0xF7FFFFFF. That might make the policy and SA match without having to explicitly remove the bit. The forecast plugin currently doesn't support masks like that (unlike the connmark plugin, for which that was added relatively recently). But that might not be necessary if you set the mark via CONNMARK rules anyway.

Now I am left with only one problem: connections to the router itself (e.g. luci or SSH) from a VPN client do not work: reply packets from the router are not delivered to the client.

There might be an issue with the marks on such traffic as it won't go through FORWARD (i.e. is not stored), but is instead handled by the rules installed by the forecast plugin in OUTPUT. So maybe the 0x08000000 bit is missing and the ESP packet is not handled correctly (see below regarding that). Maybe you could add a rule that adds it after the rules by the forecast plugin did their thing. I guess the forecast plugin would require support for masks to handle that properly so it could be configured to ignore some parts of the mark when setting the value. Although for multicast/broadcast traffic that probably won't work as that traffic is forwarded by setting a specific mark on a socket, without knowledge of the mark the original packet had (I don't think there is a way to get the mark from packets read from the AF_PACKET socket that's used to capture these packets there actually is an option for this since Linux v5.19, SO_RCVMARK).

The strange thing is I tried tcpdump -i any -nl port 9443 on the router (9443 is my luci port) and no reply is captured (same for SSH). Replies are captured instead if I visit the webpage from a LAN client.

That's normal for traffic that's encrypted via IPsec (see this FAQ entry). So if you don't see that traffic, I guess that means it gets encrypted (you can check the traffic counters on the SAs, and there might be ESP packets seen in Netfilter traces, maybe not in tcpdump, though, if the ESP is not sent correctly).

1 Like

Thank you again for your reply, btw I see you are a Strongswan developer, what an honour! :grin:

I looked multiple times and I actually don't find any possible rule, but probably it's some kind of QOS service which I haven't identified yet.

Yes, that's exactly the problem (btw the mark in your example would be 0x80000001, not 0x08000001).

Unfortunately, that one latest update is not available for my router.

OUTPUT packets seem to be not affected by the 0x80000000 mark issue, but I added the following rule just in case:
iptables -t mangle -I OUTPUT -m mark ! --mark 0x8000000 -j MARK --set-xmark 0/0x8000000

Anyway today I was trying to connect to the router's web interface from my workplace network (previously I had tried from 4G network, the only external network I had available) and everything was working (with mark=%unique). I was scratching my head thinking of why it would work vs not working from 4G network, then I remembered sometimes strange network issues appear because of MTU problems: I changed the tunnel MTU to 1280 and it worked also from 4G network!
So now everything is working!

It seems I still need something more than multicast to properly discover every DLNA device, but that's another kind of issue I guess.

EDIT1: I see no IPv6 rule is set to mark the packets by Strongswan (only IPv4), so in my case only LAN connections do not work via IPv6 because I do not have native IPv6 (so, of course, no big deal). I think it would be best to implement this in the future if you could.

EDIT2: Ok I also fixed IPv6 LAN connections (I guess it was possible thanks to the 6in4 tunnel which gets the input packets correctly marked). Rules needed overall:

Summary
iptables -t mangle -A PREROUTING -s 10.10.10.0/24 -m mark ! --mark 0x8000000 -j MARK --set-xmark 0/0x8000000
iptables -t mangle -I FORWARD ! -d 10.10.10.0/24 -m policy --dir in --pol ipsec --proto esp -j CONNMARK --save-mark
iptables -t mangle -I PREROUTING -i vlan_ptm0 -m mark --mark 0x8000000 -j CONNMARK --restore-mark
ip6tables -t mangle -A PREROUTING -s VPN_IPv6_SUBNET -m mark ! --mark 0x8000000 -j MARK --set-xmark 0/0x8000000
ip6tables -t mangle -I FORWARD ! -d VPN_IPv6_SUBNET -m policy --dir in --pol ipsec --proto esp -j CONNMARK --save-mark
ip6tables -t mangle -I INPUT -m policy --dir in --pol ipsec --proto esp -j CONNMARK --save-mark
ip6tables -t mangle -I PREROUTING -i 6in4-route48 -m mark --mark 0x0 -j CONNMARK --restore-mark
ip6tables -t mangle -I PREROUTING -i br-lan -m mark --mark 0x0 -j CONNMARK --restore-mark
ip6tables -t mangle -I OUTPUT -m mark --mark 0x0 -j CONNMARK --restore-mark

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.