mirror of
https://github.com/daeuniverse/dae.git
synced 2025-07-15 10:18:45 +07:00
feat: support bind wan and lan in the same interface
This commit is contained in:
@ -198,17 +198,33 @@ retryLoadBpf:
|
|||||||
_ = core.Close()
|
_ = core.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
// Bind to links. Binding should be advance of dialerGroups to avoid un-routable old connection.
|
/// Bind to links. Binding should be advance of dialerGroups to avoid un-routable old connection.
|
||||||
|
// Add clsact qdisc
|
||||||
|
for _, ifname := range common.Deduplicate(append(append([]string{}, lanInterface...), wanInterface...)) {
|
||||||
|
_ = core.addQdisc(ifname)
|
||||||
|
}
|
||||||
|
// Bind to LAN
|
||||||
|
if len(lanInterface) > 0 {
|
||||||
|
if err = core.setupRoutingPolicy(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
for _, ifname := range lanInterface {
|
for _, ifname := range lanInterface {
|
||||||
if err = core.bindLan(ifname); err != nil {
|
if err = core.bindLan(ifname); err != nil {
|
||||||
return nil, fmt.Errorf("bindLan: %v: %w", ifname, err)
|
return nil, fmt.Errorf("bindLan: %v: %w", ifname, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Bind to WAN
|
||||||
|
if len(wanInterface) > 0 {
|
||||||
|
if err = core.setupSkPidMonitor(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
for _, ifname := range wanInterface {
|
for _, ifname := range wanInterface {
|
||||||
if err = core.bindWan(ifname); err != nil {
|
if err = core.bindWan(ifname); err != nil {
|
||||||
return nil, fmt.Errorf("bindWan: %v: %w", ifname, err)
|
return nil, fmt.Errorf("bindWan: %v: %w", ifname, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DialerGroups (outbounds).
|
// DialerGroups (outbounds).
|
||||||
option := &dialer.GlobalOption{
|
option := &dialer.GlobalOption{
|
||||||
|
@ -12,15 +12,14 @@ import (
|
|||||||
ciliumLink "github.com/cilium/ebpf/link"
|
ciliumLink "github.com/cilium/ebpf/link"
|
||||||
"github.com/safchain/ethtool"
|
"github.com/safchain/ethtool"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/v2rayA/dae/common"
|
|
||||||
"github.com/v2rayA/dae/common/consts"
|
"github.com/v2rayA/dae/common/consts"
|
||||||
internal "github.com/v2rayA/dae/pkg/ebpf_internal"
|
internal "github.com/v2rayA/dae/pkg/ebpf_internal"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
"net/netip"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ControlPlaneCore struct {
|
type ControlPlaneCore struct {
|
||||||
@ -47,39 +46,6 @@ func (c *ControlPlaneCore) Close() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getIfParamsFromLink(link netlink.Link) (ifParams bpfIfParams, 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.
|
// Get link offload features.
|
||||||
et, err := ethtool.NewEthtool()
|
et, err := ethtool.NewEthtool()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -111,60 +77,11 @@ func getIfParamsFromLink(link netlink.Link) (ifParams bpfIfParams, err error) {
|
|||||||
return ifParams, nil
|
return ifParams, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ControlPlaneCore) bindLan(ifname string) error {
|
func (c *ControlPlaneCore) addQdisc(ifname string) error {
|
||||||
c.log.Infof("Bind to LAN: %v", ifname)
|
|
||||||
link, err := netlink.LinkByName(ifname)
|
link, err := netlink.LinkByName(ifname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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{
|
qdisc := &netlink.GenericQdisc{
|
||||||
QdiscAttrs: netlink.QdiscAttrs{
|
QdiscAttrs: netlink.QdiscAttrs{
|
||||||
LinkIndex: link.Attrs().Index,
|
LinkIndex: link.Attrs().Index,
|
||||||
@ -174,48 +91,66 @@ func (c *ControlPlaneCore) bindLan(ifname string) error {
|
|||||||
QdiscType: "clsact",
|
QdiscType: "clsact",
|
||||||
}
|
}
|
||||||
if err := netlink.QdiscAdd(qdisc); err != nil {
|
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)
|
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 nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ControlPlaneCore) bindWan(ifname string) error {
|
func (c *ControlPlaneCore) delQdisc(ifname string) error {
|
||||||
c.log.Infof("Bind to WAN: %v", ifname)
|
|
||||||
link, err := netlink.LinkByName(ifname)
|
link, err := netlink.LinkByName(ifname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if link.Attrs().Index == consts.LoopbackIfIndex {
|
qdisc := &netlink.GenericQdisc{
|
||||||
return fmt.Errorf("cannot bind to loopback interface")
|
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.
|
/// Insert an elem into IfindexParamsMap.
|
||||||
ifParams, err := getIfParamsFromLink(link)
|
ifParams, err := getIfParamsFromLink(link)
|
||||||
@ -229,6 +164,35 @@ func (c *ControlPlaneCore) bindWan(ifname string) error {
|
|||||||
return fmt.Errorf("update IfindexIpsMap: %w", err)
|
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.
|
/// Set-up SrcPidMapper.
|
||||||
/// Attach programs to support pname routing.
|
/// Attach programs to support pname routing.
|
||||||
// Get the first-mounted cgroupv2 path.
|
// Get the first-mounted cgroupv2 path.
|
||||||
@ -266,65 +230,77 @@ func (c *ControlPlaneCore) bindWan(ifname string) error {
|
|||||||
return nil
|
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.
|
/// 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
|
// Insert TC filters
|
||||||
filterEgress := &netlink.BpfFilter{
|
filterEgress := &netlink.BpfFilter{
|
||||||
FilterAttrs: netlink.FilterAttrs{
|
FilterAttrs: netlink.FilterAttrs{
|
||||||
LinkIndex: link.Attrs().Index,
|
LinkIndex: link.Attrs().Index,
|
||||||
Parent: netlink.HANDLE_MIN_EGRESS,
|
Parent: netlink.HANDLE_MIN_EGRESS,
|
||||||
Handle: netlink.MakeHandle(0, 1),
|
Handle: netlink.MakeHandle(0x2023, 1),
|
||||||
Protocol: unix.ETH_P_ALL,
|
Protocol: unix.ETH_P_ALL,
|
||||||
Priority: 0,
|
Priority: 1,
|
||||||
},
|
},
|
||||||
Fd: c.bpf.bpfPrograms.TproxyWanEgress.FD(),
|
Fd: c.bpf.bpfPrograms.TproxyWanEgress.FD(),
|
||||||
Name: consts.AppName + "_egress",
|
Name: consts.AppName + "_wan_egress",
|
||||||
DirectAction: true,
|
DirectAction: true,
|
||||||
}
|
}
|
||||||
|
// Remove and add.
|
||||||
|
_ = netlink.FilterDel(filterEgress)
|
||||||
if err := netlink.FilterAdd(filterEgress); err != nil {
|
if err := netlink.FilterAdd(filterEgress); err != nil {
|
||||||
return fmt.Errorf("cannot attach ebpf object to filter egress: %w", err)
|
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{
|
filterIngress := &netlink.BpfFilter{
|
||||||
FilterAttrs: netlink.FilterAttrs{
|
FilterAttrs: netlink.FilterAttrs{
|
||||||
LinkIndex: link.Attrs().Index,
|
LinkIndex: link.Attrs().Index,
|
||||||
Parent: netlink.HANDLE_MIN_INGRESS,
|
Parent: netlink.HANDLE_MIN_INGRESS,
|
||||||
Handle: netlink.MakeHandle(0, 1),
|
Handle: netlink.MakeHandle(0x2023, 1),
|
||||||
Protocol: unix.ETH_P_ALL,
|
Protocol: unix.ETH_P_ALL,
|
||||||
Priority: 0,
|
Priority: 1,
|
||||||
},
|
},
|
||||||
Fd: c.bpf.bpfPrograms.TproxyWanIngress.FD(),
|
Fd: c.bpf.bpfPrograms.TproxyWanIngress.FD(),
|
||||||
Name: consts.AppName + "_ingress",
|
Name: consts.AppName + "_wan_ingress",
|
||||||
DirectAction: true,
|
DirectAction: true,
|
||||||
}
|
}
|
||||||
|
// Remove and add.
|
||||||
|
_ = netlink.FilterDel(filterIngress)
|
||||||
if err := netlink.FilterAdd(filterIngress); err != nil {
|
if err := netlink.FilterAdd(filterIngress); err != nil {
|
||||||
return fmt.Errorf("cannot attach ebpf object to filter ingress: %w", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,9 @@
|
|||||||
#include "headers/socket_defs.h"
|
#include "headers/socket_defs.h"
|
||||||
#include "headers/vmlinux.h"
|
#include "headers/vmlinux.h"
|
||||||
|
|
||||||
// #include <bpf/bpf_core_read.h>
|
#include "headers/bpf_core_read.h"
|
||||||
#include "headers/bpf_endian.h"
|
#include "headers/bpf_endian.h"
|
||||||
#include "headers/bpf_helpers.h"
|
#include "headers/bpf_helpers.h"
|
||||||
#include "headers/bpf_core_read.h"
|
|
||||||
|
|
||||||
// #define __DEBUG_ROUTING
|
// #define __DEBUG_ROUTING
|
||||||
// #define __PRINT_ROUTING_RESULT
|
// #define __PRINT_ROUTING_RESULT
|
||||||
@ -190,11 +189,6 @@ struct {
|
|||||||
|
|
||||||
// Interface Ips:
|
// Interface Ips:
|
||||||
struct if_params {
|
struct if_params {
|
||||||
__be32 ip4[4];
|
|
||||||
__be32 ip6[4];
|
|
||||||
|
|
||||||
bool has_ip4;
|
|
||||||
bool has_ip6;
|
|
||||||
bool rx_cksm_offload;
|
bool rx_cksm_offload;
|
||||||
bool tx_l4_cksm_ip4_offload;
|
bool tx_l4_cksm_ip4_offload;
|
||||||
bool tx_l4_cksm_ip6_offload;
|
bool tx_l4_cksm_ip6_offload;
|
||||||
@ -220,7 +214,7 @@ struct map_lpm_type {
|
|||||||
__uint(max_entries, MAX_LPM_SIZE);
|
__uint(max_entries, MAX_LPM_SIZE);
|
||||||
__uint(key_size, sizeof(struct lpm_key));
|
__uint(key_size, sizeof(struct lpm_key));
|
||||||
__uint(value_size, sizeof(__u32));
|
__uint(value_size, sizeof(__u32));
|
||||||
} unused_lpm_type SEC(".maps"), host_ip_lpm SEC(".maps");
|
} unused_lpm_type SEC(".maps");
|
||||||
struct {
|
struct {
|
||||||
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
||||||
__uint(key_size, sizeof(__u32));
|
__uint(key_size, sizeof(__u32));
|
||||||
@ -1193,12 +1187,12 @@ int tproxy_lan_ingress(struct __sk_buff *skb) {
|
|||||||
ip rule add fwmark 0x80000000/0x80000000 table 2023
|
ip rule add fwmark 0x80000000/0x80000000 table 2023
|
||||||
ip route add local default dev lo table 2023
|
ip route add local default dev lo table 2023
|
||||||
ip -6 rule add fwmark 0x80000000/0x80000000 table 2023
|
ip -6 rule add fwmark 0x80000000/0x80000000 table 2023
|
||||||
ip -6 route add local ::/0 dev lo table 2023
|
ip -6 route add local default dev lo table 2023
|
||||||
|
|
||||||
ip rule del fwmark 0x80000000/0x80000000 table 2023
|
ip rule del fwmark 0x80000000/0x80000000 table 2023
|
||||||
ip route del local default dev lo table 2023
|
ip route del local default dev lo table 2023
|
||||||
ip -6 rule del fwmark 0x80000000/0x80000000 table 2023
|
ip -6 rule del fwmark 0x80000000/0x80000000 table 2023
|
||||||
ip -6 route del local ::/0 dev lo table 2023
|
ip -6 route del local default dev lo table 2023
|
||||||
*/
|
*/
|
||||||
// Socket lookup and assign skb to existing socket connection.
|
// Socket lookup and assign skb to existing socket connection.
|
||||||
struct bpf_sock_tuple tuple = {0};
|
struct bpf_sock_tuple tuple = {0};
|
||||||
@ -1446,13 +1440,28 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
|||||||
return TC_ACT_OK;
|
return TC_ACT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
__be16 sport;
|
// Backup for further use.
|
||||||
if (l4proto == IPPROTO_TCP) {
|
__be16 ipv4_tot_len = 0;
|
||||||
sport = tcph.source;
|
struct tuples tuples = {0};
|
||||||
} else if (l4proto == IPPROTO_UDP) {
|
tuples.l4proto = l4proto;
|
||||||
sport = udph.source;
|
if (ipversion == 4) {
|
||||||
|
tuples.src.ip[2] = bpf_htonl(0x0000ffff);
|
||||||
|
tuples.src.ip[3] = iph.saddr;
|
||||||
|
|
||||||
|
tuples.dst.ip[2] = bpf_htonl(0x0000ffff);
|
||||||
|
tuples.dst.ip[3] = iph.daddr;
|
||||||
|
|
||||||
|
ipv4_tot_len = iph.tot_len;
|
||||||
} else {
|
} else {
|
||||||
return TC_ACT_OK;
|
__builtin_memcpy(tuples.dst.ip, &ipv6h.daddr, IPV6_BYTE_LENGTH);
|
||||||
|
__builtin_memcpy(tuples.src.ip, &ipv6h.saddr, IPV6_BYTE_LENGTH);
|
||||||
|
}
|
||||||
|
if (l4proto == IPPROTO_TCP) {
|
||||||
|
tuples.src.port = tcph.source;
|
||||||
|
tuples.dst.port = tcph.dest;
|
||||||
|
} else {
|
||||||
|
tuples.src.port = udph.source;
|
||||||
|
tuples.dst.port = udph.dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should know if this packet is from tproxy.
|
// We should know if this packet is from tproxy.
|
||||||
@ -1462,29 +1471,41 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
|||||||
if (!tproxy_port) {
|
if (!tproxy_port) {
|
||||||
return TC_ACT_OK;
|
return TC_ACT_OK;
|
||||||
}
|
}
|
||||||
bool tproxy_response = *tproxy_port == sport;
|
bool tproxy_response = *tproxy_port == tuples.src.port;
|
||||||
|
// Double check to avoid bind wan and lan to one interface.
|
||||||
// Backup for further use.
|
if (tproxy_response && l4proto == IPPROTO_TCP) {
|
||||||
__be16 ipv4_tot_len = 0;
|
// If it is a TCP first handshake, it is not a tproxy response.
|
||||||
|
if (tcph.syn && !tcph.syn) {
|
||||||
// Parse saddr and daddr as ipv6 format.
|
tproxy_response = false;
|
||||||
__be32 saddr[4];
|
// Abnormal.
|
||||||
__be32 daddr[4];
|
return TC_ACT_SHOT;
|
||||||
if (ipversion == 4) {
|
|
||||||
saddr[0] = 0;
|
|
||||||
saddr[1] = 0;
|
|
||||||
saddr[2] = bpf_htonl(0x0000ffff);
|
|
||||||
saddr[3] = iph.saddr;
|
|
||||||
|
|
||||||
daddr[0] = 0;
|
|
||||||
daddr[1] = 0;
|
|
||||||
daddr[2] = bpf_htonl(0x0000ffff);
|
|
||||||
daddr[3] = iph.daddr;
|
|
||||||
|
|
||||||
ipv4_tot_len = iph.tot_len;
|
|
||||||
} else {
|
} else {
|
||||||
__builtin_memcpy(daddr, &ipv6h.daddr, IPV6_BYTE_LENGTH);
|
// If there is an existing socket on localhost, it is not a tproxy
|
||||||
__builtin_memcpy(saddr, &ipv6h.saddr, IPV6_BYTE_LENGTH);
|
// response.
|
||||||
|
struct bpf_sock_tuple tuple = {0};
|
||||||
|
__u32 tuple_size;
|
||||||
|
if (ipversion == 4) {
|
||||||
|
tuple.ipv4.daddr = tuples.dst.ip[3];
|
||||||
|
tuple.ipv4.saddr = tuples.src.ip[3];
|
||||||
|
tuple.ipv4.dport = tuples.dst.port;
|
||||||
|
tuple.ipv4.sport = tuples.src.port;
|
||||||
|
tuple_size = sizeof(tuple.ipv4);
|
||||||
|
} else {
|
||||||
|
__builtin_memcpy(tuple.ipv6.daddr, tuples.dst.ip, IPV6_BYTE_LENGTH);
|
||||||
|
__builtin_memcpy(tuple.ipv6.saddr, tuples.src.ip, IPV6_BYTE_LENGTH);
|
||||||
|
tuple.ipv6.dport = tuples.dst.port;
|
||||||
|
tuple.ipv6.sport = tuples.src.port;
|
||||||
|
tuple_size = sizeof(tuple.ipv6);
|
||||||
|
}
|
||||||
|
struct bpf_sock *sk =
|
||||||
|
bpf_skc_lookup_tcp(skb, &tuple, tuple_size, BPF_F_CURRENT_NETNS, 0);
|
||||||
|
if (sk) {
|
||||||
|
// Not a tproxy response.
|
||||||
|
tproxy_response = false;
|
||||||
|
bpf_sk_release(sk);
|
||||||
|
return TC_ACT_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tproxy_response) {
|
if (tproxy_response) {
|
||||||
@ -1511,7 +1532,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
|||||||
__builtin_memset(&key_src, 0, sizeof(key_src));
|
__builtin_memset(&key_src, 0, sizeof(key_src));
|
||||||
// Use daddr as key in WAN because tproxy (control plane) also lookups the
|
// Use daddr as key in WAN because tproxy (control plane) also lookups the
|
||||||
// map element using income client ip (that is daddr).
|
// map element using income client ip (that is daddr).
|
||||||
__builtin_memcpy(key_src.ip, daddr, IPV6_BYTE_LENGTH);
|
__builtin_memcpy(key_src.ip, tuples.dst.ip, IPV6_BYTE_LENGTH);
|
||||||
key_src.port = tcph.source;
|
key_src.port = tcph.source;
|
||||||
__u8 outbound;
|
__u8 outbound;
|
||||||
if (unlikely(tcp_state_syn)) {
|
if (unlikely(tcp_state_syn)) {
|
||||||
@ -1538,7 +1559,8 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
|||||||
bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) +
|
bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) +
|
||||||
(ethh.h_source[4] << 8) + (ethh.h_source[5])),
|
(ethh.h_source[4] << 8) + (ethh.h_source[5])),
|
||||||
};
|
};
|
||||||
if ((ret = routing(flag, &tcph, saddr, daddr, mac)) < 0) {
|
if ((ret = routing(flag, &tcph, tuples.src.ip, tuples.dst.ip, mac)) <
|
||||||
|
0) {
|
||||||
bpf_printk("shot routing: %d", ret);
|
bpf_printk("shot routing: %d", ret);
|
||||||
return TC_ACT_SHOT;
|
return TC_ACT_SHOT;
|
||||||
}
|
}
|
||||||
@ -1572,7 +1594,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
|||||||
if (unlikely(tcp_state_syn)) {
|
if (unlikely(tcp_state_syn)) {
|
||||||
struct ip_port_outbound value_dst;
|
struct ip_port_outbound value_dst;
|
||||||
__builtin_memset(&value_dst, 0, sizeof(value_dst));
|
__builtin_memset(&value_dst, 0, sizeof(value_dst));
|
||||||
__builtin_memcpy(value_dst.ip, daddr, IPV6_BYTE_LENGTH);
|
__builtin_memcpy(value_dst.ip, tuples.dst.ip, IPV6_BYTE_LENGTH);
|
||||||
value_dst.port = tcph.dest;
|
value_dst.port = tcph.dest;
|
||||||
value_dst.outbound = outbound;
|
value_dst.outbound = outbound;
|
||||||
// bpf_printk("UPDATE: %pI6:%u", key_src.ip, bpf_ntohs(key_src.port));
|
// bpf_printk("UPDATE: %pI6:%u", key_src.ip, bpf_ntohs(key_src.port));
|
||||||
@ -1595,7 +1617,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
|||||||
// Backup for further use.
|
// Backup for further use.
|
||||||
struct ip_port_outbound new_hdr;
|
struct ip_port_outbound new_hdr;
|
||||||
__builtin_memset(&new_hdr, 0, sizeof(new_hdr));
|
__builtin_memset(&new_hdr, 0, sizeof(new_hdr));
|
||||||
__builtin_memcpy(new_hdr.ip, daddr, IPV6_BYTE_LENGTH);
|
__builtin_memcpy(new_hdr.ip, tuples.dst.ip, IPV6_BYTE_LENGTH);
|
||||||
new_hdr.port = udph.dest;
|
new_hdr.port = udph.dest;
|
||||||
|
|
||||||
// Routing. It decides if we redirect traffic to control plane.
|
// Routing. It decides if we redirect traffic to control plane.
|
||||||
@ -1620,7 +1642,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
|||||||
bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) +
|
bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) +
|
||||||
(ethh.h_source[4] << 8) + (ethh.h_source[5])),
|
(ethh.h_source[4] << 8) + (ethh.h_source[5])),
|
||||||
};
|
};
|
||||||
if ((ret = routing(flag, &udph, saddr, daddr, mac)) < 0) {
|
if ((ret = routing(flag, &udph, tuples.src.ip, tuples.dst.ip, mac)) < 0) {
|
||||||
bpf_printk("shot routing: %d", ret);
|
bpf_printk("shot routing: %d", ret);
|
||||||
return TC_ACT_SHOT;
|
return TC_ACT_SHOT;
|
||||||
}
|
}
|
||||||
@ -1700,7 +1722,7 @@ int tproxy_wan_ingress(struct __sk_buff *skb) {
|
|||||||
// Tproxy related.
|
// Tproxy related.
|
||||||
__u16 tproxy_typ = bpf_ntohs(*(__u16 *)ðh.h_source[4]);
|
__u16 tproxy_typ = bpf_ntohs(*(__u16 *)ðh.h_source[4]);
|
||||||
if (*(__u32 *)ðh.h_source[0] != bpf_htonl(0x02000203) || tproxy_typ > 1) {
|
if (*(__u32 *)ðh.h_source[0] != bpf_htonl(0x02000203) || tproxy_typ > 1) {
|
||||||
return TC_ACT_OK;
|
return TC_ACT_PIPE;
|
||||||
}
|
}
|
||||||
bool tproxy_response = tproxy_typ == 1;
|
bool tproxy_response = tproxy_typ == 1;
|
||||||
|
|
||||||
@ -1773,7 +1795,6 @@ int tproxy_wan_ingress(struct __sk_buff *skb) {
|
|||||||
if (!original_dst) {
|
if (!original_dst) {
|
||||||
bpf_printk("[%X]Bad Connection: to: %pI6:%u", bpf_ntohl(tcph.seq),
|
bpf_printk("[%X]Bad Connection: to: %pI6:%u", bpf_ntohl(tcph.seq),
|
||||||
key_dst.ip, bpf_ntohs(key_dst.port));
|
key_dst.ip, bpf_ntohs(key_dst.port));
|
||||||
// Do not impact previous connections.
|
|
||||||
return TC_ACT_SHOT;
|
return TC_ACT_SHOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user