2023-01-23 18:54:21 +07:00
|
|
|
/*
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
2023-01-28 12:56:06 +07:00
|
|
|
* Copyright (c) since 2022, v2rayA Organization <team@v2raya.org>
|
2023-01-23 18:54:21 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
package control
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-01-24 15:27:19 +07:00
|
|
|
"github.com/cilium/ebpf"
|
2023-01-23 19:01:24 +07:00
|
|
|
"github.com/v2rayA/dae/common"
|
|
|
|
"github.com/v2rayA/dae/common/consts"
|
|
|
|
"github.com/v2rayA/dae/component/routing"
|
2023-01-23 18:54:21 +07:00
|
|
|
"net/netip"
|
|
|
|
"strconv"
|
|
|
|
)
|
|
|
|
|
|
|
|
type DomainSet struct {
|
|
|
|
Key string
|
|
|
|
RuleIndex int
|
|
|
|
Domains []string
|
|
|
|
}
|
2023-01-27 01:10:27 +07:00
|
|
|
|
2023-01-23 18:54:21 +07:00
|
|
|
type RoutingMatcherBuilder struct {
|
|
|
|
*routing.DefaultMatcherBuilder
|
|
|
|
outboundName2Id map[string]uint8
|
|
|
|
bpf *bpfObjects
|
|
|
|
rules []bpfRouting
|
|
|
|
SimulatedLpmTries [][]netip.Prefix
|
|
|
|
SimulatedDomainSet []DomainSet
|
|
|
|
Final string
|
|
|
|
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRoutingMatcherBuilder(outboundName2Id map[string]uint8, bpf *bpfObjects) *RoutingMatcherBuilder {
|
|
|
|
return &RoutingMatcherBuilder{outboundName2Id: outboundName2Id, bpf: bpf}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *RoutingMatcherBuilder) OutboundToId(outbound string) uint8 {
|
|
|
|
var outboundId uint8
|
|
|
|
if outbound == routing.FakeOutbound_AND {
|
|
|
|
outboundId = uint8(consts.OutboundLogicalAnd)
|
|
|
|
} else {
|
|
|
|
var ok bool
|
|
|
|
outboundId, ok = b.outboundName2Id[outbound]
|
|
|
|
if !ok {
|
|
|
|
b.err = fmt.Errorf("%v not in outboundName2Id", strconv.Quote(outbound))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return outboundId
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *RoutingMatcherBuilder) AddDomain(key string, values []string, outbound string) {
|
|
|
|
if b.err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch key {
|
|
|
|
case consts.RoutingDomain_Regex,
|
|
|
|
consts.RoutingDomain_Full,
|
|
|
|
consts.RoutingDomain_Keyword,
|
|
|
|
consts.RoutingDomain_Suffix:
|
|
|
|
default:
|
|
|
|
b.err = fmt.Errorf("AddDomain: unsupported key: %v", key)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
b.SimulatedDomainSet = append(b.SimulatedDomainSet, DomainSet{
|
|
|
|
Key: key,
|
|
|
|
RuleIndex: len(b.rules),
|
|
|
|
Domains: values,
|
|
|
|
})
|
|
|
|
b.rules = append(b.rules, bpfRouting{
|
2023-01-28 10:47:02 +07:00
|
|
|
Type: uint32(consts.RoutingType_DomainSet),
|
2023-01-23 18:54:21 +07:00
|
|
|
Outbound: b.OutboundToId(outbound),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:10:27 +07:00
|
|
|
func (b *RoutingMatcherBuilder) AddSourceMac(macAddrs [][6]byte, outbound string) {
|
2023-01-24 23:31:20 +07:00
|
|
|
if b.err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var addr16 [16]byte
|
|
|
|
values := make([]netip.Prefix, 0, len(macAddrs))
|
|
|
|
for _, mac := range macAddrs {
|
|
|
|
copy(addr16[10:], mac[:])
|
|
|
|
prefix := netip.PrefixFrom(netip.AddrFrom16(addr16), 128)
|
|
|
|
values = append(values, prefix)
|
|
|
|
}
|
|
|
|
lpmTrieIndex := len(b.SimulatedLpmTries)
|
|
|
|
b.SimulatedLpmTries = append(b.SimulatedLpmTries, values)
|
|
|
|
b.rules = append(b.rules, bpfRouting{
|
2023-01-28 10:47:02 +07:00
|
|
|
Type: uint32(consts.RoutingType_Mac),
|
2023-01-24 23:31:20 +07:00
|
|
|
Value: uint32(lpmTrieIndex),
|
|
|
|
Outbound: b.OutboundToId(outbound),
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-01-23 18:54:21 +07:00
|
|
|
func (b *RoutingMatcherBuilder) AddIp(values []netip.Prefix, outbound string) {
|
|
|
|
if b.err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
lpmTrieIndex := len(b.SimulatedLpmTries)
|
|
|
|
b.SimulatedLpmTries = append(b.SimulatedLpmTries, values)
|
|
|
|
b.rules = append(b.rules, bpfRouting{
|
2023-01-28 10:47:02 +07:00
|
|
|
Type: uint32(consts.RoutingType_IpSet),
|
2023-01-23 18:54:21 +07:00
|
|
|
Value: uint32(lpmTrieIndex),
|
|
|
|
Outbound: b.OutboundToId(outbound),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:10:27 +07:00
|
|
|
func (b *RoutingMatcherBuilder) AddSourceIp(values []netip.Prefix, outbound string) {
|
2023-01-24 23:31:20 +07:00
|
|
|
if b.err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
lpmTrieIndex := len(b.SimulatedLpmTries)
|
|
|
|
b.SimulatedLpmTries = append(b.SimulatedLpmTries, values)
|
|
|
|
b.rules = append(b.rules, bpfRouting{
|
2023-01-28 10:47:02 +07:00
|
|
|
Type: uint32(consts.RoutingType_SourceIpSet),
|
2023-01-24 23:31:20 +07:00
|
|
|
Value: uint32(lpmTrieIndex),
|
|
|
|
Outbound: b.OutboundToId(outbound),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *RoutingMatcherBuilder) AddL4Proto(values consts.L4ProtoType, outbound string) {
|
|
|
|
if b.err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
b.rules = append(b.rules, bpfRouting{
|
2023-01-28 10:47:02 +07:00
|
|
|
Type: uint32(consts.RoutingType_L4Proto),
|
2023-01-24 23:31:20 +07:00
|
|
|
Value: uint32(values),
|
|
|
|
Outbound: b.OutboundToId(outbound),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *RoutingMatcherBuilder) AddIpVersion(values consts.IpVersion, outbound string) {
|
|
|
|
if b.err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
b.rules = append(b.rules, bpfRouting{
|
2023-01-28 10:47:02 +07:00
|
|
|
Type: uint32(consts.RoutingType_IpVersion),
|
2023-01-24 23:31:20 +07:00
|
|
|
Value: uint32(values),
|
|
|
|
Outbound: b.OutboundToId(outbound),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-01-23 18:54:21 +07:00
|
|
|
func (b *RoutingMatcherBuilder) AddFinal(outbound string) {
|
2023-01-24 15:27:19 +07:00
|
|
|
if b.err != nil {
|
|
|
|
return
|
|
|
|
}
|
2023-01-23 18:54:21 +07:00
|
|
|
b.Final = outbound
|
|
|
|
b.rules = append(b.rules, bpfRouting{
|
2023-01-28 10:47:02 +07:00
|
|
|
Type: uint32(consts.RoutingType_Final),
|
2023-01-23 18:54:21 +07:00
|
|
|
Outbound: b.OutboundToId(outbound),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *RoutingMatcherBuilder) Build() (err error) {
|
|
|
|
if b.err != nil {
|
|
|
|
return b.err
|
|
|
|
}
|
|
|
|
// Update lpm_array_map.
|
|
|
|
for i, cidrs := range b.SimulatedLpmTries {
|
2023-01-28 10:47:02 +07:00
|
|
|
var keys []_bpfLpmKey
|
2023-01-23 18:54:21 +07:00
|
|
|
var values []uint32
|
|
|
|
for _, cidr := range cidrs {
|
|
|
|
keys = append(keys, cidrToBpfLpmKey(cidr))
|
|
|
|
values = append(values, 1)
|
|
|
|
}
|
2023-01-27 01:10:27 +07:00
|
|
|
m, err := b.bpf.newLpmMap(keys, values)
|
2023-01-23 18:54:21 +07:00
|
|
|
if err != nil {
|
2023-01-27 01:10:27 +07:00
|
|
|
return fmt.Errorf("newLpmMap: %w", err)
|
2023-01-23 18:54:21 +07:00
|
|
|
}
|
|
|
|
// ebpf.Map cannot be BatchUpdate
|
|
|
|
if err = b.bpf.LpmArrayMap.Update(uint32(i), m, ebpf.UpdateAny); err != nil {
|
|
|
|
m.Close()
|
|
|
|
return fmt.Errorf("Update: %w", err)
|
|
|
|
}
|
|
|
|
m.Close()
|
|
|
|
}
|
2023-01-27 01:10:27 +07:00
|
|
|
// Write routings.
|
|
|
|
// Final rule MUST be the last.
|
2023-01-28 10:47:02 +07:00
|
|
|
if b.rules[len(b.rules)-1].Type != uint32(consts.RoutingType_Final) {
|
2023-01-27 01:10:27 +07:00
|
|
|
b.err = fmt.Errorf("final rule MUST be the last")
|
|
|
|
return b.err
|
|
|
|
}
|
2023-01-23 18:54:21 +07:00
|
|
|
routingsLen := uint32(len(b.rules))
|
|
|
|
routingsKeys := common.ARangeU32(routingsLen)
|
2023-01-28 12:27:54 +07:00
|
|
|
if _, err = BatchUpdate(b.bpf.RoutingMap, routingsKeys, b.rules, &ebpf.BatchOptions{
|
2023-01-23 18:54:21 +07:00
|
|
|
ElemFlags: uint64(ebpf.UpdateAny),
|
|
|
|
}); err != nil {
|
|
|
|
return fmt.Errorf("BatchUpdate: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|