Outgoing traffic to 100.100.100.100 is not marked and should be sent through wg0.
No, if your WireGuard config file looks like this:
[Interface] FwMark = 0x1234 ... [Peer] Endpoint = 100.100.100.100:51820 AllowedIPs = 0.0.0.0/0
Then outgoing traffic sent by WireGuard to 100.100.100.100 will be marked with 0x1234 -- and therefore it will use the appropriate route from the main table.
Run this command to get the routing decision when the mark is applied:
ip route get 100.100.100.100 mark 0x1234
The Understanding modern Linux routing (and wg-quick) article provides a good explanation for how wg-quick overrides the default route on Linux. Also see the Routing All Your Traffic section of the Routing & Network Namespace Integration page on the WireGuard website.
Edit: "Regular" WireGuard doesn't involve messing with the default route -- a lot of special behavior in wg-quick is triggered when you configure WireGuard to route "everything" through it by adding a /0 network address to a peer's AllowedIPs setting. If you're trying to understand how WireGuard works, try starting out with a simple point-to-point connection.
If you use wg-quick to start up a point-to-point WireGuard connection configured like this:
# /etc/wireguard/wg0.conf [Interface] PrivateKey = ... Address = 192.168.0.2/32 [Peer] PublicKey = ... Endpoint = 100.100.100.100:51820 AllowedIPs = 192.168.0.1/32
Wg-quick will not add any special routing policy rules or packet marking; it will just add one regular route to your main routing table. The main routing table will now look something like this:
$ ip route show default via 192.168.2.1 dev eth0 proto static metric 100 192.168.0.1 dev wg0 scope link 192.168.2.0/24 dev eth0 proto kernel scope link src 192.168.2.2 metric 100
If you then ping 192.168.0.1, this is what will happen:
- Ping will use the host's network stack to send an ICMP packet to
192.168.0.1. - The network stack will make a routing decision about the ICMP packet: based on the route for
192.168.0.1 above, it will add the ICMP packet to the transmission queue for the virtual wg0 interface. - The WireGuard driver will pull the ICMP packet out of the queue, and encapsulate it inside a brand new UDP packet.
- WireGuard will use the host's network stack to send this new UDP packet to
100.100.100.100:51820. - The network stack will make a routing decision for this new UDP packet: and based on the default route above, it will add the UDP packet to the transmission queue for the physical
eth0 interface. - The Ethernet driver will encapsulate this UDP packet inside an Ethernet frame and send it out its link.
See the WireGuard Endpoints and IP Addresses article for a full end-to-end packet trace under a similar scenario.
If the wg0 interface is configured to mark packets -- which wg-quick will set up for you automatically when it encounters a /0 network address in an AllowedIPs setting, even if you don't explicitly include a FwMark setting -- the WireGuard driver will mark the encapsulating UDP packet (not the original ICMP packet) in step 4 above.
Now if you change this simple point-to-point scenario to instead route "everything" through WireGuard, using a config like this:
# /etc/wireguard/wg0.conf [Interface] PrivateKey = ... Address = 192.168.0.2/32 [Peer] PublicKey = ... Endpoint = 100.100.100.100:51820 AllowedIPs = 0.0.0.0/0
Wg-quick will add two special policy routing rules (32764 and 32765):
$ ip rule show 0: from all lookup local 32764: from all lookup main suppress_prefixlength 0 32765: not from all fwmark 0xca6c lookup 51820 32766: from all lookup main 32767: from all lookup default
And set up a custom routing table (51820):
$ ip route show table 51820 default dev wg0 scope link
If you try to ping 100.100.100.100 under this configuration, the same six steps as above will happen:
- Ping will use the host's network stack to send an ICMP packet to
100.100.100.100. - The network stack will make a routing decision about the ICMP packet -- which because it is not marked (and does not match a route with a prefix length greater than
/0 in the main table), will use the 51820 table: so based on the default route for the 51820 table, the network stack will add the ICMP packet to the transmission queue for the virtual wg0 interface. - The WireGuard driver will pull the ICMP packet out of the queue, and encapsulate it inside a brand new UDP packet.
- WireGuard will use the host's network stack to send this new UDP packet to
100.100.100.100:51820 -- sending it with a mark of 0xca6c. - The network stack will make a routing decision for this new UDP packet -- which because it is marked, will use the
main table: so based on the default route for the main table, the network stack will add the UDP packet to the transmission queue for the physical eth0 interface. - The Ethernet driver will encapsulate this UDP packet inside an Ethernet frame and send it out its link.
Edit 2: Regarding how the packet marking is done, this is how the WireGuard kernel driver does it (directly updating the packet sk_buff struct before sending a new packet off for processing by the rest of the net stack):
https://git.zx2c4.com/wireguard-linux/tree/drivers/net/wireguard/socket.c#n36
And this is how the wireguard-go driver does it (using a helper for the libc setsockopt function when it sets up the socket for a peer connection):
https://git.zx2c4.com/wireguard-go/tree/conn/mark_unix.go#n40
The libc setsockopt function (setting the SO_MARK option) is generally how user-space programs can set the packet mark on packets they generate. This does require the program to be granted either the CAP_NET_ADMIN or the CAP_NET_RAW capability, however.
ip route, and readman ip ip-route.