MagiTrickle/pkg/ip-helper/ip-helper.go

200 lines
3.9 KiB
Go
Raw Normal View History

2024-08-25 02:55:22 +03:00
package ipHelper
import (
"bufio"
"bytes"
2024-08-27 03:07:58 +03:00
"errors"
2024-08-25 02:55:22 +03:00
"fmt"
"os"
"os/exec"
"regexp"
2024-08-27 03:07:58 +03:00
"slices"
2024-08-25 02:55:22 +03:00
"strconv"
"strings"
)
2024-08-27 03:07:58 +03:00
var (
ErrMaxTableSize = errors.New("max table size")
ErrMaxFwMarkSize = errors.New("max fwmark size")
)
func ExecIp(args ...string) ([]byte, error) {
2024-08-25 02:55:22 +03:00
cmd := exec.Command("ip", args...)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return nil, err
}
return out.Bytes(), nil
}
2024-08-27 03:07:58 +03:00
func GetUsedFwMarks() ([]uint32, error) {
markMap := make(map[uint32]struct{})
2024-08-25 02:55:22 +03:00
2024-08-27 03:07:58 +03:00
out, err := ExecIp("rule", "show")
2024-08-25 02:55:22 +03:00
if err != nil {
return nil, fmt.Errorf("error while getting rules: %w", err)
}
re := regexp.MustCompile(`fwmark\s+0x([0-9a-fA-F]+)`)
for _, line := range strings.Split(string(out), "\n") {
match := re.FindStringSubmatch(line)
if len(match) < 2 {
continue
}
hexStr := match[1]
hexValue, err := strconv.ParseInt(hexStr, 16, 64)
if err == nil {
2024-08-27 03:07:58 +03:00
markMap[uint32(hexValue)] = struct{}{}
2024-08-25 02:55:22 +03:00
}
}
2024-08-27 03:07:58 +03:00
marks := make([]uint32, len(markMap))
2024-08-25 02:55:22 +03:00
counter := 0
for mark, _ := range markMap {
marks[counter] = mark
counter++
}
return marks, nil
}
2024-08-27 03:07:58 +03:00
func GetUnusedFwMark() (uint32, error) {
usedFwMarks, err := GetUsedFwMarks()
if err != nil {
return 0, fmt.Errorf("error while getting used fwmarks: %w", err)
}
fwmark := uint32(1)
for slices.Contains(usedFwMarks, fwmark) {
fwmark++
if fwmark == 0xFFFFFFFF {
return 0, ErrMaxFwMarkSize
}
}
return fwmark, nil
}
func GetTableAliases() (map[string]uint16, error) {
tables := map[string]uint16{
2024-08-25 02:55:22 +03:00
"unspec": 0,
"default": 253,
"main": 254,
"local": 255,
}
file, err := os.Open("/opt/etc/iproute2/rt_tables")
if err != nil {
if os.IsNotExist(err) {
return tables, nil
}
return nil, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "#") || len(strings.TrimSpace(line)) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) >= 2 {
tableID, err := strconv.Atoi(parts[0])
if err == nil {
2024-08-27 03:07:58 +03:00
tables[parts[1]] = uint16(tableID)
2024-08-25 02:55:22 +03:00
}
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
return tables, nil
}
2024-08-27 03:07:58 +03:00
func GetUsedTables() ([]uint16, error) {
tableMap := map[uint16]struct{}{
2024-08-25 02:55:22 +03:00
0: {},
253: {},
254: {},
255: {},
}
tableAliases, err := GetTableAliases()
if err != nil {
return nil, fmt.Errorf("error while getting table aliases: %w", err)
}
2024-08-27 03:07:58 +03:00
out, err := ExecIp("route", "show", "table", "all")
2024-08-25 02:55:22 +03:00
if err != nil {
return nil, fmt.Errorf("error while getting routes: %w", err)
}
for _, line := range strings.Split(string(out), "\n") {
if strings.Contains(line, "table") {
parts := strings.Fields(line)
for i, part := range parts {
if part == "table" && i+1 < len(parts) {
tableNum, ok := tableAliases[parts[i+1]]
if !ok {
2024-08-27 03:07:58 +03:00
tableNumInt, _ := strconv.Atoi(parts[i+1])
tableNum = uint16(tableNumInt)
}
tableMap[tableNum] = struct{}{}
}
}
}
}
out, err = ExecIp("rule", "show")
if err != nil {
return nil, fmt.Errorf("error while getting rules: %w", err)
}
for _, line := range strings.Split(string(out), "\n") {
if strings.Contains(line, "lookup") {
parts := strings.Fields(line)
for i, part := range parts {
if part == "lookup" && i+1 < len(parts) {
tableNum, ok := tableAliases[parts[i+1]]
if !ok {
tableNumInt, _ := strconv.Atoi(parts[i+1])
tableNum = uint16(tableNumInt)
2024-08-25 02:55:22 +03:00
}
tableMap[tableNum] = struct{}{}
}
}
}
}
2024-08-27 03:07:58 +03:00
tables := make([]uint16, len(tableMap))
2024-08-25 02:55:22 +03:00
counter := 0
for table, _ := range tableMap {
tables[counter] = table
counter++
}
return tables, nil
}
2024-08-27 03:07:58 +03:00
func GetUnusedTable() (uint16, error) {
usedTables, err := GetUsedTables()
if err != nil {
return 0, fmt.Errorf("error while getting used tables: %w", err)
}
tableID := uint16(1)
for slices.Contains(usedTables, tableID) {
tableID++
if tableID > 0x3FF {
return 0, ErrMaxTableSize
}
}
return tableID, nil
}