What is the right way to do port forward with multiple WANs and LANs

Hello,

Recently I start using two ISPs at home. What I do is to create two default routes, but with different distances, so when one ISP is down, another one is active. Because I have two LAN networks, I create two routing tables, so the first LAN uses mainly ISP-1 and the second LAN uses mainly ISP-2. To check ISP’s connections I use netwatch with a script that enables/disable corresponding default routes.

To force LAN’s to use only their routing table for connections to the Internet I create in IP->Routes->Rules three rules:
(1) when the destination is local networks lookup in the main routing table
(2) when the source is LAN-1 lookup only in the ISP-1 routing table
(3) when the source is LAN-2 lookup only in the ISP-2 routing table

Everything is working for me perfectly and as expected. Now it is time for port forwarding and after reading a lot I don’t know what is the right way to do. If I open some port from any of the WANs to PC in any of the LANs it is working. Return traffic from PC to the Internet goes out from the correct WAN. What confuses me is that in the topics and tutorials I read they write you should mark connection in mangle depending on the input interface and also mark packets with routing marks based on the connection mark. Then create 0.0.0.0/0 rules for marked with routing mark packets with corresponding WAN gateway.

So should I add these mangle rules and also add two default routes with routing mark? Why without them port forwarding is working in my setup?

Thanks for your help.

If LAN-1 is hardcoded to use only ISP-1, then if you forward port from ISP-2 to server in LAN-1, it will send response via ISP-1 and it can’t work.

In my case, LAN-1 use primary ISP-1, but with second default rule in its routing table for ISP-2. The second rule is with a higher distance so it is only used when first is disabled with netwatch.

Sure, it works fine when incoming request comes from ISP which is primary for server, because it will send responses there. But if you want forwarded ports working from both ISPs, that’s what the connection and route marking is for.

Strange for me, but it works in one and another case. ISP-1 is primary for the server or not. Just to be sure everything will work always can I do:

(1) in mangle for in-interface (ISP-1 interface) mark directly with some routing mark, not with connection mark first then packet mark, and finally with routing mark
(2) in mangle for in-interface (ISP-2 interface) mark directly with some routing mark, not with connection mark first then packet mark, and finally with routing mark
(3) for this routing, marks create two 0.0.0.0/0 routes with corresponding gateways

When marking directly with routing mark in the prerouting chain just to save some CPU power can I also check that marking will happen only for Connection NAT state dstnat?

It’s useless to mark routing for incoming packets, you need it for outgoing ones. Also, routing mark is per-packet, if you want something to stick to whole connection (you do), you need connection mark. You don’t need packet marks for this, just connections marks and then mark routing based on those.

You can connection-mark only dstnated connections, but you’d have to do it in forward. But you probably still want to do it in prerouting for all, because it’s useful also for accessing router itself. If for nothing else, then just to be able to ping it from outside. And I don’t think you’d save any significant amount of processing, all the heavy work is done by connection tracking in any case.

And if you say that port forwarding to same server works from both ISPs without any connection and route marking, you should export and post your config, because I don’t believe you. :slight_smile:

I don’t understand, why for outgoing packets? As I understand for all packets coming IN interface, first mark connection (Passtrought) and second mark routing. This at the prerouting chain. After that router should know that replays to these packets should go OUT from 0.0.0.0/0 rule with corresponding routing mark and gateway.

I am still stuck on your failover setup.
If you have hard coded LAN1 to ISP 1 and LAN2 to ISP 2, then why fail over??
When ISP 1 goes down, LAN1 users have nowhere to go???

Because incoming packet (from internet to server) has only one way to go, from router (where it currently is) to server. But if it’s outgoing packet (response) from server to client, router has to decide to which ISP it should send it.

The whole thing is:

  1. Packet from internet client comes to router
  2. Router marks the connection based on incoming interface (not automatically, you need to add mangle rule)
  3. Router sends packet to server in LAN
  4. Server sends response packet to router
  5. Router knows that it belongs to existing connection
  6. Router marks routing for packet based on its connection marks (another mangle rule)
  7. Router finds route for packet in given routing table, which has default gateway to same ISP from where the request came
  8. Router sends it there

Sob if I understand I should do in prerouting chain:

  1. Mark connection with some mark based on incoming interface. Passtrought.
  2. Mark packet with some mark for all connection marks from 1 Passtrough
  3. Mark routing for all packets with mark from 2. NO Passtrough
  4. Add route in main routing table.

anav I have two routing tables with TWO gateways in them. Two routes are with different distance, so when both are active only one is used. When one ISP goes down, netwatch disable one of the routes in routing table.

Step 1 can have passthrough=no, because all you need to do for incoming packets is to mark connection.
Step 2, if you mean packet mark, is not needed at all, you can go directly from connection mark to routing mark.

Check my favourite example, it’s all there: https://wiki.mikrotik.com/wiki/Manual:PCC (it’s primarily about load balancing, so just ignore the two rules with per-connection-classifier)

For such a simple thing, can it be done with IP->Route->Rules. There is not a lot of documentation about Route rules, but I use them. I think when the packet arrives at the WAN interface, the destination IP address of this packet is the WAN public IP address. In my case, both IP addresses of two WAN links are the same all the time. My ISPs never change them when the connection is restarted. So when the router sends an answer to the arrived packet, this answer will have the source IP address - public IP of the interface. In the case of port forwarding, at first, NAT will change public with private IP and then private with the same public IP.

If I have a Route rule which said - if source IP is ISP-1 public IP, send the packet from WAN-1 and the same for ISP-2. Something like this in the picture below.
mikrotik.jpg
As seen in the picture, if destination is one of local networks, router will search for route in main table / rules #0 and #1 /. But if first LAN network is the source and the destination is not another local network, router will search for route in GoceNet / ISP-1 / routing table - this is rule #4. Route rules are processed top to bottom like firewall rules. What do you think.

Routing rules work great for access to router itself (or generally with public addresses). Problem with port forwarding is that response packets from internal server have server’s internal address as source. It gets changed to public address, but it happens after routing decision.

I understand now. So I will leave rules with source IP addresses of WANs - the router will use them for example to respond pings to WANs IPs, or they are useless? But I will need also to add:

add action=mark-connection chain=prerouting comment="Mark incoming CSW connections" connection-mark=no-mark in-interface=bridge1 new-connection-mark=CSW_interface passthrough=yes
add action=mark-connection chain=prerouting comment="Mark incoming GoceNet connections" connection-mark=no-mark in-interface=pppoe-out1 new-connection-mark=GoceNet_interface passthrough=yes

add action=mark-routing chain=prerouting comment="Mark routing for CSW_interface" connection-mark=CSW_interface new-routing-mark=CSW_gateway passthrough=no
add action=mark-routing chain=prerouting comment="Mark routing for GoceNet_interface" connection-mark=GoceNet_interface new-routing-mark=GoceNet_gateway passthrough=no

In mark-connection, I set “passthrough=yes” because if I understand right MikroTik help, if “passthrough=no” mangle exit and the mark-routing rule will never be reached - right?
Should “connection-mark=no-mark” be used or it doesn’t matter in our case?
Sob, is it correct now?

Routing rules for WAN addresses are not useless, they will work. It’s just that if you already mark connections for port forwarding, then you can use same marks for this and it will be two mangle rules, or you can have two routing rules. So no difference in amount of config and some may find it better/simpler/whatever to handle everything the same way.

If bridge1 and pppoe-out1 are your WAN interfaces, then the rules are ok, they will work. First two can also have passthrough=no, because you don’t need to do anything else with incoming packets, other two rules that mark routing are only needed for outgoing ones. In your case, when you didn’t limit them with in-interface=, they now mark routing also for incoming. It’s unnecessary and it would actually break things if you didn’t have routing rules for local destinations (#0 and #1). But since you have them, there’s no problem.

The connection-mark=no-mark would not be strictly required in this config. It’s necessary when it’s used for marking new outgoing connections (like load balancing in PCC example), because without it the ourgoing rules would re-mark also incoming connections. But even in your case it probably saves some processing (a little bit), because I assume that check for connection mark should be simple and done before anything else, but it’s just a guess.

There are usually more ways how to do something. E.g. you could also go with your previous plan to mark only incoming connections to forwarded ports:

/ip firewall mangle
add chain=forward in-interface=bridge1 out-interface=<LAN> connection-state=new connection-nat-state=dstnat \
    action=mark-connection new-connection-mark=CSW_interface passthrough=no
add chain=forward in-interface=pppoe-out1 out-interface=<LAN> connection-state=new connection-nat-state=dstnat \
    action=mark-connection new-connection-mark=GoceNet_interface passthrough=no

These rules will not touch anything else. What you posted will eventually mark all connections, even outgoing ones, when first response packet comes from internet. It’s no big deal, because it doesn’t break anything, but you may not want it that way for “aesthetic reasons” for example. :slight_smile:

I want my configuration to be not only working but also working right and aesthetic. So if I understand:

  1. I mark incoming connections for two WANs. There is no need for passthrough because after marking them, mangle is left and this mark stays on packets while they exist?
  2. For every packet which has a connection mark from point 1, but only for this which came from LAN networks I need to put on them routing mark. Because my two LANs are actually VLANs on the bridge2 interface, I add “in-interface=bridge2” in mark-routing rules. Now, for more clear configuration routing rules for LAN networks and WANs IPs can be removed or not?
add action=mark-connection chain=prerouting comment="Mark incoming CSW connections" connection-mark=no-mark in-interface=bridge1 new-connection-mark=CSW_interface passthrough=no
add action=mark-connection chain=prerouting comment="Mark incoming GoceNet connections" connection-mark=no-mark in-interface=pppoe-out1 new-connection-mark=GoceNet_interface passthrough=no

add action=mark-routing chain=prerouting comment="Mark routing for CSW_interface" connection-mark=CSW_interface in-interface=bridge2 new-routing-mark=CSW_gateway passthrough=no
add action=mark-routing chain=prerouting comment="Mark routing for GoceNet_interface" connection-mark=GoceNet_interface in-interface=bridge2 new-routing-mark=GoceNet_gateway passthrough=no

add check-gateway=ping comment="Use CSW for routing mark CSW_gateway" distance=1 gateway=77.xx.xx.113 routing-mark=CSW_gateway
add check-gateway=ping comment="Use GoceNet for routing mark GoceNet_gateway" distance=1 gateway=pppoe-out1 routing-mark=GoceNet_gateway

P.S. Now I read one old topic, where Sob explains for connections marking. If I understand correctly, my rules mark all packets in for example WAN-1 interface. Not only ingoing but also outgoing and this is the problem. So to “sort” them we will apply routing marks only on packets which have connection mark but on this of them which come from LAN networks (outgoing packets). Firstly I think that with the in-interface parameter only incoming packets will be market - this on which we need to reply.

First important thing to understand is that there isn’t the perfect universal solution. There can be various differences in setups and each may require something slightly different (e.g things like static or dynamic address can influence a lot). Even when it’s same, it’s often possible to use different ways to reach the goal. You can also use various shortcuts and there’s nothing wrong with them, it’s useless to create “perfect” config that covers everything, when you won’t really need some of that at all.

Other thing that helps is understanding how it works. At least rought idea about packet flow is a must. Then there’s connection tracking, which is part of stateful firewall. Router examines every single packet and tries to put them together, so it knows what belongs to what. When there’s e.g. packet from client:12345 to server:80 and then response from server:80 to client:12345, router understands that they belong to same connection. Connection tracking happens regardles of you using any marks or not, because it’s required for stateful firewall (connection-state option) or NAT. You can optionally use connection marks, if you need to work with some connections. What happens is that router then automatically assigns same mark to all packets of same connection.

About your points:

  1. There is no need for passthrough for incoming packets, because marking connection is all you need for them (actually just the first packet). They then go to internal server and nothing special is required for that.

  2. It won’t work with in-interface=bridge2, because on IP level the VLANs are incoming interfaces. You’d need two rules for each WAN and that twice, because you also have two LANs, so you’d need to match connections:

  • from LAN1 with WAN1 mark
  • from LAN1 with WAN2 mark
  • from LAN2 with WAN1 mark
  • from LAN2 with WAN2 mark

You can see that it’s getting complicated. Imagine that you’d have even more LANs or WANs. So it may actually be better to use what you had before, i.e. don’t care about source, just match connections:

  • with WAN1 mark
  • with WAN2 mark

As I wrote before, this would break routing for incoming packets, but if you have routing rules telling router that route to local subnets should be looked up in main routing table, it fixes the problem. It’s a compromise, one part may look less clean, but other part can make the whole thing simpler. It’s up to you, what you find better, there’s not just one proper solution.

And you know what’s the most funny part? It’s possible that you don’t need any mangle rules at all. At the beginning you wrote that everything works, even when you don’t have them. So it depends on what exactly you do. The point of this whole thing is to allow to use port forwarding with multiple WANs. But default simple example has only one LAN and one server. It uses one WAN as default for outgoing traffic and you need to override it for connections that came from another WAN.

This applies in your case if you need to forward port from WANx to 192.168.0.x and also port from other WANy to same 192.168.0.x. If you do this, you need this connection and route marking. But if you forward port from WANx to 192.168.0.x and don’t forward any port from WANy to it, and forward another port from WANy to 192.168.254.x but no port from WANx to it, i.e. forwarded ports are only from WAN which target server uses as default, then you don’t need this at all, because your previous routing rules are enough.

Yes, there is no perfect solution for all cases, I fully agree. I want to create a more universal one.

I understand you for interfaces, so instead of “in-interface” I create an interface list and use “in-interface-list” in mark-routing rules. For now, I add two LAN networks interfaces (vlan10 and vlan20) in this list, and in the future, if I add more LAN networks it will be very easy to add their interfaces in this interface list and not touching mangle. This is what I mean under “more universal configuration”.

One more step. In my case, WANs IPs are the same all the time and the routing rule which said use WAN-1 when the source IP is IP-1 and then use WAN-2 when the source IP is IP-2 will work. But if WANs IPs change from time to time there is a problem. To solve it and to make the configuration more universal, can I duplicate “mark-connection” rules for the “input” chain and duplicate “mark-routing” rules for the “output” chain?

add action=mark-connection chain=prerouting comment="Mark incoming CSW connections" connection-mark=no-mark in-interface=bridge1 new-connection-mark=CSW_interface passthrough=no
add action=mark-connection chain=input comment="Mark incoming CSW connections" connection-mark=no-mark in-interface=bridge1 new-connection-mark=CSW_interface passthrough=no

add action=mark-connection chain=prerouting comment="Mark incoming GoceNet connections" connection-mark=no-mark in-interface=pppoe-out1 new-connection-mark=GoceNet_interface passthrough=no
add action=mark-connection chain=input comment="Mark incoming GoceNet connections" connection-mark=no-mark in-interface=pppoe-out1 new-connection-mark=GoceNet_interface passthrough=no


add action=mark-routing chain=prerouting comment="Mark routing for CSW_interface" connection-mark=CSW_interface in-interface-list=mangleList new-routing-mark=CSW_gateway passthrough=no
add action=mark-routing chain=output comment="Mark routing for CSW_interface" connection-mark=CSW_interface new-routing-mark=CSW_gateway passthrough=no

add action=mark-routing chain=prerouting comment="Mark routing for GoceNet_interface" connection-mark=GoceNet_interface in-interface-list=mangleList new-routing-mark=GoceNet_gateway passthrough=no
add action=mark-routing chain=output comment="Mark routing for GoceNet_interface" connection-mark=GoceNet_interface new-routing-mark=GoceNet_gateway passthrough=no

add check-gateway=ping comment="Use CSW for routing mark CSW_gateway" distance=1 gateway=77.xx.xx.113 routing-mark=CSW_gateway
add check-gateway=ping comment="Use GoceNet for routing mark GoceNet_gateway" distance=1 gateway=pppoe-out1 routing-mark=GoceNet_gateway

You don’t need to mark connections in input, it’s already covered by marking in prerouting, which happens before input.

When you mark routing in output, it’s compatible with dynamic addresses. Also, for the purpose of responding back to same WAN, this is enough and you don’t need routing rules for WAN addresses.

But they are still not necessarily useless. If something on router would initiate connection to internet and it would set source address of one WAN interface, these rules would automatically make packets go the right way. You can test it e.g with “/ping src-address= address=”. But it’s possible that you don’t really need it for anything.

You’ll also need to adjust your routing rules, because last two will make packets from given subnet always use routing table for same WAN, regardless of routing mark. These before last two should do the trick:

/ip route rule
add routing-mark=CSW_gateway table=CSW_interface
add routing-mark=GoceNet_gateway table=GoceNet_interface

Sob, I don’t understand the last one. Because I don’t have “table=CSW_interface” and “table=GoceNet_interface”, did you mean that I should have a routing rule, which said: for every packet with routing mark “x” use table “x” and for every packet with routing mark “y” use table “y”?

/ip route rule
add action=lookup-only-in-table routing-mark=CSW_gateway table=CSW_gateway
add action=lookup-only-in-table routing-mark=GoceNet_gateway table=GoceNet_gateway