mirror of
https://github.com/daeuniverse/dae.git
synced 2025-07-10 07:48:48 +07:00
feat: support bind wan and lan in the same interface
This commit is contained in:
@ -12,15 +12,14 @@ import (
|
||||
ciliumLink "github.com/cilium/ebpf/link"
|
||||
"github.com/safchain/ethtool"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/v2rayA/dae/common"
|
||||
"github.com/v2rayA/dae/common/consts"
|
||||
internal "github.com/v2rayA/dae/pkg/ebpf_internal"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.org/x/sys/unix"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ControlPlaneCore struct {
|
||||
@ -47,39 +46,6 @@ func (c *ControlPlaneCore) Close() (err error) {
|
||||
}
|
||||
|
||||
func getIfParamsFromLink(link netlink.Link) (ifParams bpfIfParams, err error) {
|
||||
// TODO: We should monitor IP change of the link.
|
||||
ipnets, err := netlink.AddrList(link, netlink.FAMILY_ALL)
|
||||
if err != nil {
|
||||
return bpfIfParams{}, err
|
||||
}
|
||||
if len(ipnets) == 0 {
|
||||
return bpfIfParams{}, fmt.Errorf("interface %v has no ip", link.Attrs().Name)
|
||||
}
|
||||
// Get first Ip4 and Ip6.
|
||||
for _, ipnet := range ipnets {
|
||||
ip, ok := netip.AddrFromSlice(ipnet.IP)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
||||
continue
|
||||
}
|
||||
if (ip.Is6() && ifParams.HasIp6) ||
|
||||
(ip.Is4() && ifParams.HasIp4) {
|
||||
continue
|
||||
}
|
||||
ip6format := ip.As16()
|
||||
if ip.Is4() {
|
||||
ifParams.HasIp4 = true
|
||||
ifParams.Ip4 = common.Ipv6ByteSliceToUint32Array(ip6format[:])
|
||||
} else {
|
||||
ifParams.HasIp6 = true
|
||||
ifParams.Ip6 = common.Ipv6ByteSliceToUint32Array(ip6format[:])
|
||||
}
|
||||
if ifParams.HasIp4 && ifParams.HasIp6 {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Get link offload features.
|
||||
et, err := ethtool.NewEthtool()
|
||||
if err != nil {
|
||||
@ -111,60 +77,11 @@ func getIfParamsFromLink(link netlink.Link) (ifParams bpfIfParams, err error) {
|
||||
return ifParams, nil
|
||||
}
|
||||
|
||||
func (c *ControlPlaneCore) bindLan(ifname string) error {
|
||||
c.log.Infof("Bind to LAN: %v", ifname)
|
||||
func (c *ControlPlaneCore) addQdisc(ifname string) error {
|
||||
link, err := netlink.LinkByName(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
/// Insert ip rule / ip route.
|
||||
var output []byte
|
||||
if output, err = exec.Command("sh", "-c", `
|
||||
ip rule add fwmark 0x80000000/0x80000000 table 2023
|
||||
ip route add local default dev lo table 2023
|
||||
ip -6 rule add fwmark 0x80000000/0x80000000 table 2023
|
||||
ip -6 route add local default dev lo table 2023
|
||||
`).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("%w: %v", err, string(bytes.TrimSpace(output)))
|
||||
}
|
||||
c.deferFuncs = append(c.deferFuncs, func() error {
|
||||
return exec.Command("sh", "-c", `
|
||||
ip rule del fwmark 0x80000000/0x80000000 table 2023
|
||||
ip route del local default dev lo table 2023
|
||||
ip -6 rule del fwmark 0x80000000/0x80000000 table 2023
|
||||
ip -6 route del local default dev lo table 2023
|
||||
`).Run()
|
||||
})
|
||||
/// Insert an elem into IfindexParamsMap.
|
||||
ifParams, err := getIfParamsFromLink(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ifParams.CheckVersionRequirement(c.kernelVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.bpf.IfindexParamsMap.Update(uint32(link.Attrs().Index), ifParams, ebpf.UpdateAny); err != nil {
|
||||
return fmt.Errorf("update IfindexIpsMap: %w", err)
|
||||
}
|
||||
// FIXME: not only this link ip.
|
||||
if ifParams.HasIp4 {
|
||||
if err := c.bpf.HostIpLpm.Update(_bpfLpmKey{
|
||||
PrefixLen: 128,
|
||||
Data: ifParams.Ip4,
|
||||
}, uint32(1), ebpf.UpdateAny); err != nil {
|
||||
return fmt.Errorf("update IfindexIpsMap: %w", err)
|
||||
}
|
||||
}
|
||||
if ifParams.HasIp6 {
|
||||
if err := c.bpf.HostIpLpm.Update(_bpfLpmKey{
|
||||
PrefixLen: 128,
|
||||
Data: ifParams.Ip6,
|
||||
}, uint32(1), ebpf.UpdateAny); err != nil {
|
||||
return fmt.Errorf("update IfindexIpsMap: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Insert qdisc and filters.
|
||||
qdisc := &netlink.GenericQdisc{
|
||||
QdiscAttrs: netlink.QdiscAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
@ -174,48 +91,66 @@ func (c *ControlPlaneCore) bindLan(ifname string) error {
|
||||
QdiscType: "clsact",
|
||||
}
|
||||
if err := netlink.QdiscAdd(qdisc); err != nil {
|
||||
if os.IsExist(err) {
|
||||
_ = netlink.QdiscDel(qdisc)
|
||||
err = netlink.QdiscAdd(qdisc)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot add clsact qdisc: %w", err)
|
||||
}
|
||||
}
|
||||
c.deferFuncs = append(c.deferFuncs, func() error {
|
||||
if err := netlink.QdiscDel(qdisc); err != nil {
|
||||
return fmt.Errorf("QdiscDel: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
filterIngress := &netlink.BpfFilter{
|
||||
FilterAttrs: netlink.FilterAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Parent: netlink.HANDLE_MIN_INGRESS,
|
||||
Handle: netlink.MakeHandle(0, 1),
|
||||
Protocol: unix.ETH_P_ALL,
|
||||
Priority: 0,
|
||||
},
|
||||
Fd: c.bpf.bpfPrograms.TproxyLanIngress.FD(),
|
||||
Name: consts.AppName + "_ingress",
|
||||
DirectAction: true,
|
||||
}
|
||||
if err := netlink.FilterAdd(filterIngress); err != nil {
|
||||
return fmt.Errorf("cannot attach ebpf object to filter ingress: %w", err)
|
||||
return fmt.Errorf("cannot add clsact qdisc: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ControlPlaneCore) bindWan(ifname string) error {
|
||||
c.log.Infof("Bind to WAN: %v", ifname)
|
||||
func (c *ControlPlaneCore) delQdisc(ifname string) error {
|
||||
link, err := netlink.LinkByName(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if link.Attrs().Index == consts.LoopbackIfIndex {
|
||||
return fmt.Errorf("cannot bind to loopback interface")
|
||||
qdisc := &netlink.GenericQdisc{
|
||||
QdiscAttrs: netlink.QdiscAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Handle: netlink.MakeHandle(0xffff, 0),
|
||||
Parent: netlink.HANDLE_CLSACT,
|
||||
},
|
||||
QdiscType: "clsact",
|
||||
}
|
||||
if err := netlink.QdiscDel(qdisc); err != nil {
|
||||
if !os.IsExist(err) {
|
||||
return fmt.Errorf("cannot add clsact qdisc: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ControlPlaneCore) setupRoutingPolicy() (err error) {
|
||||
/// Insert ip rule / ip route.
|
||||
// TODO: Refactor me with netlink.
|
||||
var output []byte
|
||||
cleanFunc := func() error {
|
||||
return exec.Command("sh", "-c", `
|
||||
ip rule del fwmark 0x80000000/0x80000000 table 2023
|
||||
ip route del local default dev lo table 2023
|
||||
ip -6 rule del fwmark 0x80000000/0x80000000 table 2023
|
||||
ip -6 route del local default dev lo table 2023
|
||||
`).Run()
|
||||
}
|
||||
tryAgain:
|
||||
if output, err = exec.Command("sh", "-c", `
|
||||
ip rule add fwmark 0x80000000/0x80000000 table 2023
|
||||
ip route add local default dev lo table 2023
|
||||
ip -6 rule add fwmark 0x80000000/0x80000000 table 2023
|
||||
ip -6 route add local default dev lo table 2023
|
||||
`).CombinedOutput(); err != nil {
|
||||
if strings.Contains(string(output), "File exists") {
|
||||
_ = cleanFunc()
|
||||
goto tryAgain
|
||||
}
|
||||
return fmt.Errorf("%w: %v", err, string(bytes.TrimSpace(output)))
|
||||
}
|
||||
c.deferFuncs = append(c.deferFuncs, cleanFunc)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ControlPlaneCore) bindLan(ifname string) error {
|
||||
c.log.Infof("Bind to LAN: %v", ifname)
|
||||
link, err := netlink.LinkByName(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
/// Insert an elem into IfindexParamsMap.
|
||||
ifParams, err := getIfParamsFromLink(link)
|
||||
@ -229,6 +164,35 @@ func (c *ControlPlaneCore) bindWan(ifname string) error {
|
||||
return fmt.Errorf("update IfindexIpsMap: %w", err)
|
||||
}
|
||||
|
||||
// Insert filters.
|
||||
filterIngress := &netlink.BpfFilter{
|
||||
FilterAttrs: netlink.FilterAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Parent: netlink.HANDLE_MIN_INGRESS,
|
||||
Handle: netlink.MakeHandle(0x2023, 2),
|
||||
Protocol: unix.ETH_P_ALL,
|
||||
// Priority should be behind of WAN's
|
||||
Priority: 2,
|
||||
},
|
||||
Fd: c.bpf.bpfPrograms.TproxyLanIngress.FD(),
|
||||
Name: consts.AppName + "_lan_ingress",
|
||||
DirectAction: true,
|
||||
}
|
||||
// Remove and add.
|
||||
_ = netlink.FilterDel(filterIngress)
|
||||
if err := netlink.FilterAdd(filterIngress); err != nil {
|
||||
return fmt.Errorf("cannot attach ebpf object to filter ingress: %w", err)
|
||||
}
|
||||
c.deferFuncs = append(c.deferFuncs, func() error {
|
||||
if err := netlink.FilterDel(filterIngress); err != nil {
|
||||
return fmt.Errorf("FilterDel(%v:%v): %w", ifname, filterIngress.Name, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ControlPlaneCore) setupSkPidMonitor() error {
|
||||
/// Set-up SrcPidMapper.
|
||||
/// Attach programs to support pname routing.
|
||||
// Get the first-mounted cgroupv2 path.
|
||||
@ -266,65 +230,77 @@ func (c *ControlPlaneCore) bindWan(ifname string) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (c *ControlPlaneCore) bindWan(ifname string) error {
|
||||
c.log.Infof("Bind to WAN: %v", ifname)
|
||||
link, err := netlink.LinkByName(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if link.Attrs().Index == consts.LoopbackIfIndex {
|
||||
return fmt.Errorf("cannot bind to loopback interface")
|
||||
}
|
||||
/// Insert an elem into IfindexParamsMap.
|
||||
ifParams, err := getIfParamsFromLink(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ifParams.CheckVersionRequirement(c.kernelVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.bpf.IfindexParamsMap.Update(uint32(link.Attrs().Index), ifParams, ebpf.UpdateAny); err != nil {
|
||||
return fmt.Errorf("update IfindexIpsMap: %w", err)
|
||||
}
|
||||
|
||||
/// Set-up WAN ingress/egress TC programs.
|
||||
// Insert qdisc.
|
||||
qdisc := &netlink.GenericQdisc{
|
||||
QdiscAttrs: netlink.QdiscAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Handle: netlink.MakeHandle(0xffff, 0),
|
||||
Parent: netlink.HANDLE_CLSACT,
|
||||
},
|
||||
QdiscType: "clsact",
|
||||
}
|
||||
if err := netlink.QdiscAdd(qdisc); err != nil {
|
||||
if os.IsExist(err) {
|
||||
_ = netlink.QdiscDel(qdisc)
|
||||
err = netlink.QdiscAdd(qdisc)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot add clsact qdisc: %w", err)
|
||||
}
|
||||
}
|
||||
c.deferFuncs = append(c.deferFuncs, func() error {
|
||||
if err := netlink.QdiscDel(qdisc); err != nil {
|
||||
return fmt.Errorf("QdiscDel: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Insert TC filters
|
||||
filterEgress := &netlink.BpfFilter{
|
||||
FilterAttrs: netlink.FilterAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Parent: netlink.HANDLE_MIN_EGRESS,
|
||||
Handle: netlink.MakeHandle(0, 1),
|
||||
Handle: netlink.MakeHandle(0x2023, 1),
|
||||
Protocol: unix.ETH_P_ALL,
|
||||
Priority: 0,
|
||||
Priority: 1,
|
||||
},
|
||||
Fd: c.bpf.bpfPrograms.TproxyWanEgress.FD(),
|
||||
Name: consts.AppName + "_egress",
|
||||
Name: consts.AppName + "_wan_egress",
|
||||
DirectAction: true,
|
||||
}
|
||||
// Remove and add.
|
||||
_ = netlink.FilterDel(filterEgress)
|
||||
if err := netlink.FilterAdd(filterEgress); err != nil {
|
||||
return fmt.Errorf("cannot attach ebpf object to filter egress: %w", err)
|
||||
}
|
||||
c.deferFuncs = append(c.deferFuncs, func() error {
|
||||
if err := netlink.FilterDel(filterEgress); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("FilterDel(%v:%v): %w", ifname, filterEgress.Name, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
filterIngress := &netlink.BpfFilter{
|
||||
FilterAttrs: netlink.FilterAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Parent: netlink.HANDLE_MIN_INGRESS,
|
||||
Handle: netlink.MakeHandle(0, 1),
|
||||
Handle: netlink.MakeHandle(0x2023, 1),
|
||||
Protocol: unix.ETH_P_ALL,
|
||||
Priority: 0,
|
||||
Priority: 1,
|
||||
},
|
||||
Fd: c.bpf.bpfPrograms.TproxyWanIngress.FD(),
|
||||
Name: consts.AppName + "_ingress",
|
||||
Name: consts.AppName + "_wan_ingress",
|
||||
DirectAction: true,
|
||||
}
|
||||
// Remove and add.
|
||||
_ = netlink.FilterDel(filterIngress)
|
||||
if err := netlink.FilterAdd(filterIngress); err != nil {
|
||||
return fmt.Errorf("cannot attach ebpf object to filter ingress: %w", err)
|
||||
}
|
||||
c.deferFuncs = append(c.deferFuncs, func() error {
|
||||
if err := netlink.FilterDel(filterIngress); err != nil {
|
||||
return fmt.Errorf("FilterDel(%v:%v): %w", ifname, filterIngress.Name, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user