pkg/*: allow kgctl to compile for other OSes
This commit enables the compilation of kgctl when GOOS!=linux. This fixes #56. Signed-off-by: Lucas Servén Marín <lserven@gmail.com>
This commit is contained in:
252
pkg/mesh/routes.go
Normal file
252
pkg/mesh/routes.go
Normal file
@@ -0,0 +1,252 @@
|
||||
// Copyright 2019 the Kilo authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build linux
|
||||
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/squat/kilo/pkg/encapsulation"
|
||||
"github.com/squat/kilo/pkg/iptables"
|
||||
)
|
||||
|
||||
const kiloTableIndex = 1107
|
||||
|
||||
// Routes generates a slice of routes for a given Topology.
|
||||
func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface int, local bool, enc encapsulation.Encapsulator) ([]*netlink.Route, []*netlink.Rule) {
|
||||
var routes []*netlink.Route
|
||||
var rules []*netlink.Rule
|
||||
if !t.leader {
|
||||
// Find the GW for this segment.
|
||||
// This will be the an IP of the leader.
|
||||
// In an IPIP encapsulated mesh it is the leader's private IP.
|
||||
var gw net.IP
|
||||
for _, segment := range t.segments {
|
||||
if segment.location == t.location {
|
||||
gw = enc.Gw(segment.endpoint.IP, segment.privateIPs[segment.leader], segment.cidrs[segment.leader])
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, segment := range t.segments {
|
||||
// First, add a route to the WireGuard IP of the segment.
|
||||
routes = append(routes, encapsulateRoute(&netlink.Route{
|
||||
Dst: oneAddressCIDR(segment.wireGuardIP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: gw,
|
||||
LinkIndex: privIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
}, enc.Strategy(), t.privateIP, tunlIface))
|
||||
// Add routes for the current segment if local is true.
|
||||
if segment.location == t.location {
|
||||
if local {
|
||||
for i := range segment.cidrs {
|
||||
// Don't add routes for the local node.
|
||||
if segment.privateIPs[i].Equal(t.privateIP.IP) {
|
||||
continue
|
||||
}
|
||||
routes = append(routes, encapsulateRoute(&netlink.Route{
|
||||
Dst: segment.cidrs[i],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: segment.privateIPs[i],
|
||||
LinkIndex: privIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
}, enc.Strategy(), t.privateIP, tunlIface))
|
||||
// Encapsulate packets from the host's Pod subnet headed
|
||||
// to private IPs.
|
||||
if enc.Strategy() == encapsulation.Always || (enc.Strategy() == encapsulation.CrossSubnet && !t.privateIP.Contains(segment.privateIPs[i])) {
|
||||
routes = append(routes, &netlink.Route{
|
||||
Dst: oneAddressCIDR(segment.privateIPs[i]),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: segment.privateIPs[i],
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
Table: kiloTableIndex,
|
||||
})
|
||||
rules = append(rules, defaultRule(&netlink.Rule{
|
||||
Src: t.subnet,
|
||||
Dst: oneAddressCIDR(segment.privateIPs[i]),
|
||||
Table: kiloTableIndex,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
for i := range segment.cidrs {
|
||||
// Add routes to the Pod CIDRs of nodes in other segments.
|
||||
routes = append(routes, encapsulateRoute(&netlink.Route{
|
||||
Dst: segment.cidrs[i],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: gw,
|
||||
LinkIndex: privIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
}, enc.Strategy(), t.privateIP, tunlIface))
|
||||
// Add routes to the private IPs of nodes in other segments.
|
||||
// Number of CIDRs and private IPs always match so
|
||||
// we can reuse the loop.
|
||||
routes = append(routes, encapsulateRoute(&netlink.Route{
|
||||
Dst: oneAddressCIDR(segment.privateIPs[i]),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: gw,
|
||||
LinkIndex: privIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
}, enc.Strategy(), t.privateIP, tunlIface))
|
||||
}
|
||||
}
|
||||
// Add routes for the allowed IPs of peers.
|
||||
for _, peer := range t.peers {
|
||||
for i := range peer.AllowedIPs {
|
||||
routes = append(routes, encapsulateRoute(&netlink.Route{
|
||||
Dst: peer.AllowedIPs[i],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: gw,
|
||||
LinkIndex: privIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
}, enc.Strategy(), t.privateIP, tunlIface))
|
||||
}
|
||||
}
|
||||
return routes, rules
|
||||
}
|
||||
for _, segment := range t.segments {
|
||||
// Add routes for the current segment if local is true.
|
||||
if segment.location == t.location {
|
||||
if local {
|
||||
for i := range segment.cidrs {
|
||||
// Don't add routes for the local node.
|
||||
if segment.privateIPs[i].Equal(t.privateIP.IP) {
|
||||
continue
|
||||
}
|
||||
routes = append(routes, encapsulateRoute(&netlink.Route{
|
||||
Dst: segment.cidrs[i],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: segment.privateIPs[i],
|
||||
LinkIndex: privIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
}, enc.Strategy(), t.privateIP, tunlIface))
|
||||
// Encapsulate packets from the host's Pod subnet headed
|
||||
// to private IPs.
|
||||
if enc.Strategy() == encapsulation.Always || (enc.Strategy() == encapsulation.CrossSubnet && !t.privateIP.Contains(segment.privateIPs[i])) {
|
||||
routes = append(routes, &netlink.Route{
|
||||
Dst: oneAddressCIDR(segment.privateIPs[i]),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: segment.privateIPs[i],
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
Table: kiloTableIndex,
|
||||
})
|
||||
rules = append(rules, defaultRule(&netlink.Rule{
|
||||
Src: t.subnet,
|
||||
Dst: oneAddressCIDR(segment.privateIPs[i]),
|
||||
Table: kiloTableIndex,
|
||||
}))
|
||||
// Also encapsulate packets from the Kilo interface
|
||||
// headed to private IPs.
|
||||
rules = append(rules, defaultRule(&netlink.Rule{
|
||||
Dst: oneAddressCIDR(segment.privateIPs[i]),
|
||||
Table: kiloTableIndex,
|
||||
IifName: kiloIfaceName,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
for i := range segment.cidrs {
|
||||
// Add routes to the Pod CIDRs of nodes in other segments.
|
||||
routes = append(routes, &netlink.Route{
|
||||
Dst: segment.cidrs[i],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: segment.wireGuardIP,
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
})
|
||||
// Don't add routes through Kilo if the private IP
|
||||
// equals the external IP. This means that the node
|
||||
// is only accessible through an external IP and we
|
||||
// cannot encapsulate traffic to an IP through the IP.
|
||||
if segment.privateIPs[i].Equal(segment.endpoint.IP) {
|
||||
continue
|
||||
}
|
||||
// Add routes to the private IPs of nodes in other segments.
|
||||
// Number of CIDRs and private IPs always match so
|
||||
// we can reuse the loop.
|
||||
routes = append(routes, &netlink.Route{
|
||||
Dst: oneAddressCIDR(segment.privateIPs[i]),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: segment.wireGuardIP,
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
})
|
||||
}
|
||||
}
|
||||
// Add routes for the allowed IPs of peers.
|
||||
for _, peer := range t.peers {
|
||||
for i := range peer.AllowedIPs {
|
||||
routes = append(routes, &netlink.Route{
|
||||
Dst: peer.AllowedIPs[i],
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
})
|
||||
}
|
||||
}
|
||||
return routes, rules
|
||||
}
|
||||
|
||||
func encapsulateRoute(route *netlink.Route, encapsulate encapsulation.Strategy, subnet *net.IPNet, tunlIface int) *netlink.Route {
|
||||
if encapsulate == encapsulation.Always || (encapsulate == encapsulation.CrossSubnet && !subnet.Contains(route.Gw)) {
|
||||
route.LinkIndex = tunlIface
|
||||
}
|
||||
return route
|
||||
}
|
||||
|
||||
// Rules returns the iptables rules required by the local node.
|
||||
func (t *Topology) Rules(cni bool) []iptables.Rule {
|
||||
var rules []iptables.Rule
|
||||
rules = append(rules, iptables.NewIPv4Chain("nat", "KILO-NAT"))
|
||||
rules = append(rules, iptables.NewIPv6Chain("nat", "KILO-NAT"))
|
||||
if cni {
|
||||
rules = append(rules, iptables.NewRule(iptables.GetProtocol(len(t.subnet.IP)), "nat", "POSTROUTING", "-m", "comment", "--comment", "Kilo: jump to NAT chain", "-s", t.subnet.String(), "-j", "KILO-NAT"))
|
||||
}
|
||||
for _, s := range t.segments {
|
||||
rules = append(rules, iptables.NewRule(iptables.GetProtocol(len(s.wireGuardIP)), "nat", "KILO-NAT", "-m", "comment", "--comment", "Kilo: do not NAT packets destined for WireGuared IPs", "-d", s.wireGuardIP.String(), "-j", "RETURN"))
|
||||
for _, aip := range s.allowedIPs {
|
||||
rules = append(rules, iptables.NewRule(iptables.GetProtocol(len(aip.IP)), "nat", "KILO-NAT", "-m", "comment", "--comment", "Kilo: do not NAT packets destined for known IPs", "-d", aip.String(), "-j", "RETURN"))
|
||||
}
|
||||
}
|
||||
for _, p := range t.peers {
|
||||
for _, aip := range p.AllowedIPs {
|
||||
rules = append(rules,
|
||||
iptables.NewRule(iptables.GetProtocol(len(aip.IP)), "nat", "POSTROUTING", "-m", "comment", "--comment", "Kilo: jump to NAT chain", "-s", aip.String(), "-j", "KILO-NAT"),
|
||||
iptables.NewRule(iptables.GetProtocol(len(aip.IP)), "nat", "KILO-NAT", "-m", "comment", "--comment", "Kilo: do not NAT packets destined for peers", "-d", aip.String(), "-j", "RETURN"),
|
||||
)
|
||||
}
|
||||
}
|
||||
rules = append(rules, iptables.NewIPv4Rule("nat", "KILO-NAT", "-m", "comment", "--comment", "Kilo: NAT remaining packets", "-j", "MASQUERADE"))
|
||||
rules = append(rules, iptables.NewIPv6Rule("nat", "KILO-NAT", "-m", "comment", "--comment", "Kilo: NAT remaining packets", "-j", "MASQUERADE"))
|
||||
return rules
|
||||
}
|
||||
|
||||
func defaultRule(rule *netlink.Rule) *netlink.Rule {
|
||||
base := netlink.NewRule()
|
||||
base.Src = rule.Src
|
||||
base.Dst = rule.Dst
|
||||
base.IifName = rule.IifName
|
||||
base.Table = rule.Table
|
||||
return base
|
||||
}
|
Reference in New Issue
Block a user