OpenWrt Forum Archive

Topic: Iptables mangle by string

The content of this topic has been archived on 31 Mar 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

Hello,

I wanted to route specific web pages over my VPN. I am using iptables mangle and mark for this. For example:

iptables -t mangle -A PREROUTING -p tcp --dport 1234 -d 1.2.3.4 -j MARK --set-mark 2

This would let everything to 1.2.3.4:1234 go other mark 2, which is one of my VPN connections. It is working flawless.

But now I want to do the same for specific internet pages, like youtube. I read somewhere it is possible to let iptables filter by strings, but I can't get it working. I am using this right now:

iptables -t mangle -A PREROUTING -p tcp -m string --algo kmp --string 'youtube' -j MARK --set-mark 2

Anything wrong with this? I can't open youtube anymore by the time I am using this rule.

cant you just use the FQDN: youtube.com as destination?

Edit: how does the rest of your config look like?

(Last edited by kirschwasser on 18 Jul 2013, 23:23)

You coult take a look at ipset-dns, tunnel dns lookups to specific domains through it using dns server directives, then use the resulting populated ipset to mark your traffic.

Here's the config I use at home to route youtube over an openvpn link:

root@uplink:~# cat /etc/config/openvpn | grep -vE '^[[:space:]]*#' | grep -vE '^$'
package openvpn

config openvpn madrid
    option enabled 1
    option config /etc/openvpn/gateway-madrid.conf
root@uplink:~# cat /etc/openvpn/gateway-madrid.conf
client
dev gw-madrid
dev-type tap
proto tcp
remote example.org 443
resolv-retry infinite
nobind
persist-key
persist-tun
cd /etc/openvpn/gateway-madrid
ca ca.crt
cert gateway-home.crt
key gateway-home.key
comp-lzo
verb 3
root@uplink:~# cat /etc/iproute2/rt_tables 
#
# reserved values
#
255    local
254    main
253    default
0    unspec
#
# local
#
1    vpn
root@uplink:~# cat /etc/config/network

[...]

config interface 'vpn'
    option proto 'static'
    option ipaddr '10.8.0.2'
    option netmask '255.255.255.0'
    option ip6addr 'fd73:c6fb:ef9::3/64'
    option ifname 'gw-madrid'

config route
    option interface 'vpn'
    option netmask '0.0.0.0'
    option gateway '10.8.0.1'
    option table 'vpn'
    option target '0.0.0.0'

config route6
    option interface 'vpn'
    option target '::/0'
    option gateway 'fd73:c6fb:ef9::2'
    option table 'vpn'

config rule # ip -4 rule add mark 0x1 lookup vpn
    option mark '0x1'
    option lookup 'vpn'

config rule6 # ip -6 rule add mark 0x1 lookup vpn
    option mark '0x1'
    option lookup 'vpn'
root@uplink:~# cat /etc/config/firewall

[...]

config ipset
    option name 'domain-filter-ipv4'
    option match 'dest_ip'
    option storage 'hash'
    option family 'IPv4'

config ipset
    option name 'domain-filter-ipv6'
    option match 'dest_ip'
    option storage 'hash'
    option family 'IPv6'

config rule
    option src 'lan'
    option target 'MARK'
    option set_mark '0x1'
    option name 'Tag Domain Filter Traffic'
    option family 'IPv4'
    option ipset 'domain-filter-ipv4'
    option proto 'tcp udp icmp'

config rule
    option src 'lan'
    option target 'MARK'
    option set_mark '0x1'
    option name 'Tag Domain Filter Traffic'
    option family 'IPv6'
    option ipset 'domain-filter-ipv6'
    option proto 'tcp udp icmp'

config redirect
    option name 'Divert DNS'
    option src 'lan'
    option proto 'tcp udp'
    option src_dport '53'
    option dest_port '53'
    option target 'DNAT'
root@uplink:~# cat /etc/config/ipset-dns 
config ipset-dns
    option ipset 'domain-filter-ipv4'
    option ipset6 'domain-filter-ipv6'
    option dns   '8.8.8.8'
root@uplink:~# cat /etc/config/dhcp

config dnsmasq
    [...]
    list server '/youtube.com/127.0.0.1#53001'
    
[...]

@kirschwasser This doesn't work, should it?
iptables -t mangle -A PREROUTING -p tcp -d youtube.com -j MARK --set-mark 2

So I guess I have to use that ipset-dns methode. I installed ipset and ipset-dns, but I can't get it to work, here's my script I use right now, that I found on another page:

sets() {
    iptables -t mangle -D PREROUTING -m set --match-set "$1" dst,src -j MARK --set-mark "$2" 2>/dev/null
    ipset -X "$1" 2>/dev/null
    ipset -N "$1" iphash
    iptables -t mangle -A PREROUTING -m set --match-set "$1" dst,src -j MARK --set-mark "$2"
} # I edited it from "-m set --set" (was so in the original script, but gave me an error) to "-m set --match-set"

sets youtube 2
sets netflix 3

killall ipset-dns 2>/dev/null

ipset-dns youtube youtube 39128 8.8.8.8
ipset-dns netflix netflix 39129 8.8.8.8
# I edited this, because the script just said ipset-dns youtube 39128 8.8.8.8, gave me the error "Usage: ipset-dns ipv4-ipset ipv6-ipset port upstream"

killall -SIGHUP dnsmasq

----

Whats wrong with this? ip rules are the following, already set before I got into this whole youtube/netflix routing idea, vpn's are working of course:

ip rule add fwmark 2 table vpn1
ip rule add fwmark 3 table vpn2

and

ip route add default via $GW dev tun0 table vpn1
iptables -t nat -A POSTROUTING -o tun0 -j SNAT --to $IP
ip route add default via $GW dev tun1 table vpn2
iptables -t nat -A POSTROUTING -o tun1 -j SNAT --to $IP

iptables -t mangle -A PREROUTING -s 10.0.0.0/24 -j MARK --set-mark 2

And in /etc/dnsmasq.conf:

server=/c.youtube.com/127.0.0.1#39128
server=/netflix.com/127.0.0.1#39129

(Last edited by knuddel on 19 Jul 2013, 04:45)

iptables -t mangle -A PREROUTING -p tcp -d youtube.com -j MARK --set-mark 2
works for me. So does the solution presented at http://git.zx2c4.com/ipset-dns/about/ and so does the solution presented by jow.

WTF is your problem?

knuddel: I patched ipset-dns in OpenWrt to take two setname arguments instead of just one in the original code.
The first ipset is used for IPv4 ips, the 2nd for IPv6 so if you use the script suggested by its author you need to change the lines

ipset-dns youtube youtube 39128 8.8.8.8
ipset-dns netflix netflix 39129 8.8.8.8

to

ipset-dns youtube youtube "" 39128 8.8.8.8
ipset-dns netflix netflix "" 39129 8.8.8.8

kirschwasser: the problem with using dns names in iptables commands is that those domains are resolved only once when the rule is inserted, but sites like youtube have many different ip ranges which change randomly and it is impossible to add rules for every possible range used.

@kirschwasser What did I do to upset you? Maybe say nothing if you want to say it in an unfriendly way? Sorry that I am not a Linux guru and I asked for help here.

@jow Thank you a lot for helping. I tried changing what you suggested but that was not causing me trouble. I changed it to:

ipset-dns youtube "" 39128 8.8.8.8
ipset-dns netflix "" 39129 8.8.8.8
ipset-dns whatsmyip "" 39130 8.8.8.8

I added a 3rd to debug. But when I visit them I am routed over my normal configuration, not the vpn. Also when I type in ipset -L youtube, it shows me:

Name: youtube
Type: hash:ip
Revision: 0
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 8252
References: 1
Members: #shoudlnt it contain the ip here?

So I guess theres something wrong? Also I want to use it this way via manuell config, I don't want to "build" it into the openwrt configs, because I have all my vpn settings as manuells already.

Is there any openwrt package btw containing the "host" dns lookup tool?

(Last edited by knuddel on 19 Jul 2013, 16:07)

I got it to work, but I have some smaller tuning problems now. For example, a problem is, that when I just use /youtube.com/ the ipset contains a lot of IPs which are not just youtube, for example the Google 1e100.net servers, which are used for every other Google services too. The author of ipset-dns used /c.youtube.com/ in his config, so I guess that means something like a limitation filter to the domain? Whats the difference between /c.youtube.com/ and /youtube.com/? Or did Youtube change something on their links, was it xyz.c.youtube.com for the video sources before but not anymore? I tried using /c.youtube.com/, but then youtube won't work anymore for me, and videos are blocked because of country IP. I guess the video source is behind xyz.c, but the ip block is before that on youtube.com

What happens if I "exclude" 1e100.net with an extra ipset? like

server=/1e100.net/127.0.0.1#39130

What happens when two IPs are in more than one ipset? Would the last one be used?

Also, would these ipset's contain every IP on a domain youtube.com, like amazon cloud IPs, or facebook links and such? That would be a little bit contra productive.

(Last edited by knuddel on 23 Jul 2013, 20:00)

Try it out and report back. Would be nice to have a fully workable howto present.

@jow: I've followed the setup you've detailed (post #7) and I've got an ipset that is populating correctly.

I'm guessing to complete this, I need to use an iptable rule to route the following traffic matching the ipset via the VPN and to send all other traffic via my ISP directly, right? If so, how would I go about doing that?

Looking at examples elsewhere, I'm guessing the iptable rule is supposed to look something like this:

#iptables -A PREROUTING -t mangle -m set --match-set NAME_OF_IPSET dst -i VPN_INTERFACE -p TCP -j MARK --set-mark 120 

But then if the above iptable rule routes traffic matching the ipset via the VPN, will I need a second iptable rule that sends all other traffic not through the VPN?

The discussion might have continued from here.