netset: add nftables set support
Some checks failed
Build / Build (push) Has been cancelled

This commit is contained in:
nadoo 2026-07-15 23:41:21 +08:00
parent 6a7fc56a5e
commit 43552b60c9
17 changed files with 504 additions and 126 deletions

View File

@ -5,7 +5,7 @@
[![Actions Status](https://img.shields.io/github/actions/workflow/status/nadoo/glider/build.yml?branch=dev&style=flat-square)](https://github.com/nadoo/glider/actions)
[![DockerHub](https://img.shields.io/docker/image-size/nadoo/glider?color=blue&label=docker&style=flat-square)](https://hub.docker.com/r/nadoo/glider)
glider is a forward proxy with multiple protocols support, and also a dns/dhcp server with ipset management features.
glider is a forward proxy with multiple protocols support, and also a dns/dhcp server with ipset and nftables set management features.
we can set up local listeners as proxy servers, and forward requests to internet via forwarders.
@ -28,10 +28,10 @@ we can set up local listeners as proxy servers, and forward requests to internet
- dns over proxy
- force upstream querying by tcp
- association rules between dns and forwarder choosing
- association rules between dns and ipset
- association rules between dns and ipset/nftables sets
- dns cache support
- custom dns record
- IPSet management (linux kernel version >= 2.6.32):
- IPSet and nftables set management on Linux:
- add ip/cidrs from rule files on startup
- add resolved ips for domains from rule files by dns forwarding server
- Serve http and socks5 on the same port
@ -541,7 +541,7 @@ glider -config CONFIG_PATH
## Links
- [ipset](https://github.com/nadoo/ipset): netlink ipset package for Go.
- [netset](https://github.com/nadoo/netset): netlink ipset and nftables set package for Go.
- [conflag](https://github.com/nadoo/conflag): a drop-in replacement for Go's standard flag package with config file support.
- [ArchLinux](https://archlinux.org/packages/extra/x86_64/glider): a great linux distribution with glider pre-built package.
- [urlencode](https://www.w3schools.com/tags/ref_urlencode.asp): you should encode special characters in scheme url. e.g., `@`->`%40`

View File

@ -73,14 +73,22 @@ checkinterval=30
# DNS SERVER for domains in this rule file
dnsserver=208.67.222.222:53
# IPSET MANAGEMENT
# ----------------
# Create and mange ipset on linux based on destinations in rule files
# NETFILTER SET MANAGEMENT
# ------------------------
# Create and manage ipset or nftables sets on linux based on destinations in rule files
# - add ip/cidrs in rule files on startup
# - add resolved ips for domains in rule files by dns forwarding server
# Usually used in transparent proxy mode on linux
# This creates glider for ipv4 and glider6 for ipv6
ipset=glider
# nftset uses [[FAMILY/]TABLE/]NAME. FAMILY defaults to inet and TABLE to glider.
# The nftables table must already exist. This also creates NAME and NAME6.
# nftset=glider
# nftset=custom_table/glider
# nftset=inet/custom_table/glider
# ipset and nftset can both be enabled in the same rule file during migration.
# YOU CAN SPECIFY DESTINATIONS TO USE THE ABOVE FORWARDERS
# matches abc.com and *.abc.com
domain=abc.com

View File

@ -6,7 +6,7 @@ PC Client -> Gateway with glider running(linux box) -> Upstream Forwarders -> In
#### In this mode, glider will act as the following roles:
1. A transparent proxy server
2. A dns forwarding server
3. A ipset manager
3. An ipset or nftables set manager
so you don't need any dns server in your network.
@ -76,6 +76,27 @@ iptables -t nat -I PREROUTING -p tcp -m set --match-set glider dst -j REDIRECT -
iptables -t nat -I OUTPUT -p tcp -m set --match-set glider dst -j REDIRECT --to-ports 1081
```
#### Nftables alternative
Use `nftset=glider` instead of `ipset=glider` in the rule file. It means
`inet/glider/glider` for IPv4 and `inet/glider/glider6` for IPv6. The table
must already exist:
```bash
nft add table inet glider
```
Glider creates both interval sets and flushes them on startup. If nftables
rules are loaded before glider starts, create compatible sets first:
```bash
nft 'add set inet glider glider { type ipv4_addr; flags interval; }'
nft 'add set inet glider glider6 { type ipv6_addr; flags interval; }'
nft 'add chain inet glider prerouting { type nat hook prerouting priority dstnat; policy accept; }'
nft 'add rule inet glider prerouting ip daddr @glider tcp redirect to :1081'
nft 'add rule inet glider prerouting ip6 daddr @glider6 tcp redirect to :1081'
```
#### Server DNS settings
Set server's nameserver to glider:
```bash

View File

@ -3,7 +3,7 @@
# / /`_ | | | | | | \ | |_ | |_)
# \_\_/ |_|__ |_| |_|_/ |_|__ |_| \
#
# Glider is a forward proxy with multiple protocols support, and also a dns forwarding server with ipset management features(like dnsmasq).
# Glider is a forward proxy with multiple protocols support, and also a dns forwarding server with ipset and nftables set management features(like dnsmasq).
#
# We can set up local listeners as proxy, and forward requests to internet via forwarders.
#

View File

@ -20,15 +20,19 @@ checkinterval=30
# DNS SERVER for domains in this rule file
dnsserver=208.67.222.222:53
# IPSET MANAGEMENT
# ----------------
# Create and mange ipset on linux based on destinations in rule files
# NETFILTER SET MANAGEMENT
# ------------------------
# Create and manage ipset or nftables sets on linux based on destinations in rule files
# - add ip/cidrs in rule files on startup
# - add resolved ips for domains in rule files by dns forwarding server
# Usually used in transparent proxy mode on linux
# Note: this will create 2 ipsets, glider for ipv4 and glider6 for ipv6
ipset=glider
# nftset uses [[FAMILY/]TABLE/]NAME. FAMILY defaults to inet and TABLE to glider.
# The nftables table must already exist. This creates glider and glider6.
# nftset=glider
# DESTINATIONS
# ------------
# ALL destinations matches the following rules will be forward using forwarders specified above

View File

@ -143,7 +143,9 @@ func (c *Client) extractAnswer(resp *Message) ([]string, int) {
if answer.TYPE == QTypeA || answer.TYPE == QTypeAAAA {
if answer.IP.IsValid() && !answer.IP.IsUnspecified() {
for _, h := range c.handlers {
h(resp.Question.QNAME, answer.IP)
if err := h(resp.Question.QNAME, answer.IP); err != nil {
log.F("[dns] answer handler error for %s/%s: %s", resp.Question.QNAME, answer.IP, err)
}
}
ips = append(ips, answer.IP.String())
}

2
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
github.com/insomniacslk/dhcp v0.0.0-20260603135910-a415979eb11e
github.com/nadoo/conflag v0.3.1
github.com/nadoo/ipset v0.5.0
github.com/nadoo/netset v0.1.0
github.com/xtaci/kcp-go/v5 v5.6.72
golang.org/x/crypto v0.54.0
golang.org/x/sys v0.47.0

4
go.sum
View File

@ -39,8 +39,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/nadoo/conflag v0.3.1 h1:4pHkLIz8PUsfg6ajNYRRSY3bt6m2LPsu6KOzn5uIXQw=
github.com/nadoo/conflag v0.3.1/go.mod h1:dzFfDUpXdr2uS2oV+udpy5N2vfNOu/bFzjhX1WI52co=
github.com/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
github.com/nadoo/netset v0.1.0 h1:FNdGCVtoSjBX0Ctzve3BWwTOSXRxK/oSPBzS3/SemY4=
github.com/nadoo/netset v0.1.0/go.mod h1:w2GPCnz+TxqH+HJIrhOEC4oZAeQ5Crefw4lToBIxEww=
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

View File

@ -1,78 +0,0 @@
package ipset
import (
"net/netip"
"strings"
"sync"
"github.com/nadoo/ipset"
"github.com/nadoo/glider/rule"
)
// Manager struct.
type Manager struct {
domainSet sync.Map
}
// NewManager returns a Manager
func NewManager(rules []*rule.Config) (*Manager, error) {
if err := ipset.Init(); err != nil {
return nil, err
}
m := &Manager{}
sets := make(map[string]struct{})
for _, r := range rules {
if r.IPSet == "" {
continue
}
if _, ok := sets[r.IPSet]; !ok {
sets[r.IPSet] = struct{}{}
ipset.Create(r.IPSet)
ipset.Flush(r.IPSet)
ipset.Create(r.IPSet+"6", ipset.OptIPv6())
ipset.Flush(r.IPSet + "6")
}
for _, domain := range r.Domain {
m.domainSet.Store(domain, r.IPSet)
}
for _, ip := range r.IP {
addToSet(r.IPSet, ip)
}
for _, cidr := range r.CIDR {
addToSet(r.IPSet, cidr)
}
}
return m, nil
}
// AddDomainIP implements the dns AnswerHandler function, used to update ipset according to domainSet rule.
func (m *Manager) AddDomainIP(domain string, ip netip.Addr) error {
domain = strings.ToLower(domain)
for i := len(domain); i != -1; {
i = strings.LastIndexByte(domain[:i], '.')
if setName, ok := m.domainSet.Load(domain[i+1:]); ok {
addAddrToSet(setName.(string), ip)
}
}
return nil
}
func addToSet(s, item string) error {
if strings.IndexByte(item, '.') == -1 {
return ipset.Add(s+"6", item)
}
return ipset.Add(s, item)
}
func addAddrToSet(s string, ip netip.Addr) error {
if ip.Is4() {
return ipset.AddAddr(s, ip)
}
return ipset.AddAddr(s+"6", ip)
}

View File

@ -1,23 +0,0 @@
//go:build !linux
package ipset
import (
"errors"
"net/netip"
"github.com/nadoo/glider/rule"
)
// Manager struct
type Manager struct{}
// NewManager returns a Manager
func NewManager(rules []*rule.Config) (*Manager, error) {
return nil, errors.New("ipset not supported on this os")
}
// AddDomainIP implements the DNSAnswerHandler function
func (m *Manager) AddDomainIP(domain string, ip netip.Addr) error {
return errors.New("ipset not supported on this os")
}

17
main.go
View File

@ -9,7 +9,7 @@ import (
"time"
"github.com/nadoo/glider/dns"
"github.com/nadoo/glider/ipset"
"github.com/nadoo/glider/netset"
"github.com/nadoo/glider/pkg/log"
"github.com/nadoo/glider/proxy"
"github.com/nadoo/glider/rule"
@ -25,8 +25,15 @@ func main() {
// global rule proxy
pxy := rule.NewProxy(config.Forwards, &config.Strategy, config.rules)
// ipset manager
ipsetM, _ := ipset.NewManager(config.rules)
// ipset and nftables set manager
netsetM, _ := netset.NewManager(config.rules)
if netsetM != nil {
defer func() {
if err := netsetM.Close(); err != nil {
log.F("[netset] close error: %s", err)
}
}()
}
// check and setup dns server
if config.DNS != "" {
@ -46,8 +53,8 @@ func main() {
// add a handler to update proxy rules when a domain resolved
d.AddHandler(pxy.AddDomainIP)
if ipsetM != nil {
d.AddHandler(ipsetM.AddDomainIP)
if netsetM != nil {
d.AddHandler(netsetM.AddDomainIP)
}
d.Start()

92
netset/config.go Normal file
View File

@ -0,0 +1,92 @@
package netset
import (
"fmt"
"strings"
libnetset "github.com/nadoo/netset"
)
const (
defaultNFTFamily = "inet"
defaultNFTTable = "glider"
maxNFTNameLength = 256
)
type nftSetConfig struct {
family libnetset.Family
familyName string
table string
name string
}
func parseNFTSetConfig(value string) (nftSetConfig, error) {
parts := strings.Split(strings.TrimSpace(value), "/")
for i := range parts {
parts[i] = strings.TrimSpace(parts[i])
}
config := nftSetConfig{
family: libnetset.FamilyINet,
familyName: defaultNFTFamily,
table: defaultNFTTable,
}
switch len(parts) {
case 1:
config.name = parts[0]
case 2:
config.table = parts[0]
config.name = parts[1]
case 3:
config.familyName = parts[0]
config.table = parts[1]
config.name = parts[2]
default:
return nftSetConfig{}, fmt.Errorf("invalid nftset %q: expected [[FAMILY/]TABLE/]NAME", value)
}
if config.familyName == "" || config.table == "" || config.name == "" {
return nftSetConfig{}, fmt.Errorf("invalid nftset %q: family, table, and set name must not be empty", value)
}
family, ok := nftFamilies[strings.ToLower(config.familyName)]
if !ok {
return nftSetConfig{}, fmt.Errorf("invalid nftset %q: unsupported family %q", value, config.familyName)
}
config.family = family
config.familyName = strings.ToLower(config.familyName)
if err := validateNFTName("table", config.table); err != nil {
return nftSetConfig{}, fmt.Errorf("invalid nftset %q: %w", value, err)
}
if err := validateNFTName("set", config.name); err != nil {
return nftSetConfig{}, fmt.Errorf("invalid nftset %q: %w", value, err)
}
if err := validateNFTName("IPv6 set", config.name+"6"); err != nil {
return nftSetConfig{}, fmt.Errorf("invalid nftset %q: %w", value, err)
}
return config, nil
}
func validateNFTName(kind, name string) error {
if strings.IndexByte(name, 0) >= 0 {
return fmt.Errorf("%s name must not contain NUL", kind)
}
if len(name) >= maxNFTNameLength {
return fmt.Errorf("%s name is too long", kind)
}
return nil
}
func (c nftSetConfig) key() string {
return c.familyName + "/" + c.table + "/" + c.name
}
var nftFamilies = map[string]libnetset.Family{
"inet": libnetset.FamilyINet,
"ip": libnetset.FamilyIPv4,
"ip6": libnetset.FamilyIPv6,
"arp": libnetset.FamilyARP,
"netdev": libnetset.FamilyNetdev,
"bridge": libnetset.FamilyBridge,
}

200
netset/manager.go Normal file
View File

@ -0,0 +1,200 @@
package netset
import (
"errors"
"fmt"
"net/netip"
"strings"
"github.com/nadoo/glider/rule"
)
type addressSet interface {
AddAddr(netip.Addr) error
AddPrefix(netip.Prefix) error
}
type setPair struct {
label string
ipv4 addressSet
ipv6 addressSet
}
func (p *setPair) addAddr(ip netip.Addr) error {
if ip.Is4() {
return p.ipv4.AddAddr(ip)
}
return p.ipv6.AddAddr(ip)
}
func (p *setPair) addPrefix(prefix netip.Prefix) error {
if prefix.Addr().Is4() {
return p.ipv4.AddPrefix(prefix)
}
return p.ipv6.AddPrefix(prefix)
}
type backend interface {
IPSetPair(name string) (*setPair, error)
NFTSetPair(config nftSetConfig) (*setPair, error)
Close() error
}
// Manager manages the ipset and nftables sets associated with rules.
type Manager struct {
backend backend
domainSets map[string][]*setPair
pairs map[string]*setPair
}
// NewManager creates and populates all sets configured by rules.
func NewManager(rules []*rule.Config) (*Manager, error) {
if !hasSetConfig(rules) {
return nil, nil
}
b, err := newBackend()
if err != nil {
return nil, err
}
return newManager(rules, b)
}
func newManager(rules []*rule.Config, b backend) (*Manager, error) {
m := &Manager{
backend: b,
domainSets: make(map[string][]*setPair),
pairs: make(map[string]*setPair),
}
for _, config := range rules {
if err := m.addRule(config); err != nil {
return nil, errors.Join(err, b.Close())
}
}
return m, nil
}
func hasSetConfig(rules []*rule.Config) bool {
for _, config := range rules {
if config.IPSet != "" || config.NFTSet != "" {
return true
}
}
return false
}
func (m *Manager) addRule(config *rule.Config) error {
pairs := make([]*setPair, 0, 2)
if config.IPSet != "" {
pair, err := m.pair("ipset:"+config.IPSet, func() (*setPair, error) {
return m.backend.IPSetPair(config.IPSet)
})
if err != nil {
return fmt.Errorf("rule %q: initialize ipset %q: %w", config.RulePath, config.IPSet, err)
}
pairs = append(pairs, pair)
}
if config.NFTSet != "" {
nftConfig, err := parseNFTSetConfig(config.NFTSet)
if err != nil {
return fmt.Errorf("rule %q: %w", config.RulePath, err)
}
pair, err := m.pair("nftset:"+nftConfig.key(), func() (*setPair, error) {
return m.backend.NFTSetPair(nftConfig)
})
if err != nil {
return fmt.Errorf("rule %q: initialize nftset %q: %w", config.RulePath, nftConfig.key(), err)
}
pairs = append(pairs, pair)
}
for _, item := range config.IP {
ip, err := netip.ParseAddr(item)
if err != nil {
return fmt.Errorf("rule %q: parse set IP %q: %w", config.RulePath, item, err)
}
for _, pair := range pairs {
if err := pair.addAddr(ip); err != nil {
return fmt.Errorf("rule %q: add IP %q to %s: %w", config.RulePath, item, pair.label, err)
}
}
}
for _, item := range config.CIDR {
prefix, err := netip.ParsePrefix(item)
if err != nil {
return fmt.Errorf("rule %q: parse set CIDR %q: %w", config.RulePath, item, err)
}
for _, pair := range pairs {
if err := pair.addPrefix(prefix); err != nil {
return fmt.Errorf("rule %q: add CIDR %q to %s: %w", config.RulePath, item, pair.label, err)
}
}
}
for _, domain := range config.Domain {
domain = strings.ToLower(domain)
for _, pair := range pairs {
m.domainSets[domain] = appendPair(m.domainSets[domain], pair)
}
}
return nil
}
func (m *Manager) pair(key string, create func() (*setPair, error)) (*setPair, error) {
if pair, ok := m.pairs[key]; ok {
return pair, nil
}
pair, err := create()
if err != nil {
return nil, err
}
m.pairs[key] = pair
return pair, nil
}
func appendPair(pairs []*setPair, pair *setPair) []*setPair {
for _, item := range pairs {
if item == pair {
return pairs
}
}
return append(pairs, pair)
}
// AddDomainIP updates all sets associated with domain or one of its parents.
func (m *Manager) AddDomainIP(domain string, ip netip.Addr) error {
if !ip.IsValid() {
return errors.New("netset: invalid DNS answer address")
}
domain = strings.ToLower(domain)
seen := make(map[*setPair]struct{})
var errs []error
for i := len(domain); i != -1; {
i = strings.LastIndexByte(domain[:i], '.')
for _, pair := range m.domainSets[domain[i+1:]] {
if _, ok := seen[pair]; ok {
continue
}
seen[pair] = struct{}{}
if err := pair.addAddr(ip); err != nil {
errs = append(errs, fmt.Errorf("add DNS answer %s for %q to %s: %w", ip, domain, pair.label, err))
}
}
}
return errors.Join(errs...)
}
// Close closes all netlink sockets owned by the manager.
func (m *Manager) Close() error {
if m == nil || m.backend == nil {
return nil
}
return m.backend.Close()
}

130
netset/manager_linux.go Normal file
View File

@ -0,0 +1,130 @@
//go:build linux
package netset
import (
"errors"
"fmt"
"net/netip"
"syscall"
libnetset "github.com/nadoo/netset"
)
type linuxBackend struct {
ipset *libnetset.IPSet
nftsets []*libnetset.NFTSet
}
func newBackend() (backend, error) {
return &linuxBackend{}, nil
}
func (b *linuxBackend) IPSetPair(name string) (*setPair, error) {
if b.ipset == nil {
client, err := libnetset.NewIPSet()
if err != nil {
return nil, err
}
b.ipset = client
}
if err := ensureIPSet(b.ipset, name); err != nil {
return nil, err
}
if err := ensureIPSet(b.ipset, name+"6", libnetset.OptIPv6()); err != nil {
return nil, err
}
return &setPair{
label: "ipset " + name,
ipv4: ipSetTarget{client: b.ipset, name: name},
ipv6: ipSetTarget{client: b.ipset, name: name + "6"},
}, nil
}
func ensureIPSet(client *libnetset.IPSet, name string, opts ...libnetset.Option) error {
if err := client.Create(name, opts...); err != nil && !errors.Is(err, syscall.EEXIST) {
return fmt.Errorf("create %q: %w", name, err)
}
if err := client.Flush(name); err != nil {
return fmt.Errorf("flush %q: %w", name, err)
}
return nil
}
func (b *linuxBackend) NFTSetPair(config nftSetConfig) (*setPair, error) {
ipv4, err := b.newNFTSet(config, config.name, false)
if err != nil {
return nil, err
}
ipv6, err := b.newNFTSet(config, config.name+"6", true)
if err != nil {
return nil, err
}
return &setPair{
label: "nftset " + config.key(),
ipv4: nftSetTarget{set: ipv4},
ipv6: nftSetTarget{set: ipv6},
}, nil
}
func (b *linuxBackend) newNFTSet(config nftSetConfig, name string, ipv6 bool) (*libnetset.NFTSet, error) {
opts := []libnetset.Option{libnetset.OptFamily(config.family), libnetset.OptInterval()}
if ipv6 {
opts = append(opts, libnetset.OptIPv6())
}
set, err := libnetset.NewNFTSet(config.table, name, opts...)
if err != nil {
return nil, fmt.Errorf("open %s/%s/%s: %w", config.familyName, config.table, name, err)
}
b.nftsets = append(b.nftsets, set)
if err := set.Create(); err != nil && !errors.Is(err, syscall.EEXIST) {
return nil, fmt.Errorf("create %s/%s/%s: %w", config.familyName, config.table, name, err)
}
if err := set.Flush(); err != nil {
return nil, fmt.Errorf("flush %s/%s/%s: %w", config.familyName, config.table, name, err)
}
return set, nil
}
func (b *linuxBackend) Close() error {
var errs []error
if b.ipset != nil {
errs = append(errs, b.ipset.Close())
b.ipset = nil
}
for _, set := range b.nftsets {
errs = append(errs, set.Close())
}
b.nftsets = nil
return errors.Join(errs...)
}
type ipSetTarget struct {
client *libnetset.IPSet
name string
}
func (s ipSetTarget) AddAddr(ip netip.Addr) error {
return s.client.AddAddr(s.name, ip)
}
func (s ipSetTarget) AddPrefix(prefix netip.Prefix) error {
return s.client.AddPrefix(s.name, prefix)
}
type nftSetTarget struct {
set *libnetset.NFTSet
}
func (s nftSetTarget) AddAddr(ip netip.Addr) error {
return s.set.AddAddr(ip)
}
func (s nftSetTarget) AddPrefix(prefix netip.Prefix) error {
return s.set.AddPrefix(prefix)
}

13
netset/manager_other.go Normal file
View File

@ -0,0 +1,13 @@
//go:build !linux
package netset
import (
"fmt"
libnetset "github.com/nadoo/netset"
)
func newBackend() (backend, error) {
return nil, fmt.Errorf("netset manager is supported only on linux: %w", libnetset.ErrUnsupported)
}

View File

@ -16,6 +16,7 @@ type Config struct {
DNSServers []string
IPSet string
NFTSet string
Domain []string
IP []string
@ -57,6 +58,7 @@ func NewConfFromFile(ruleFile string) (*Config, error) {
f.StringSliceUniqVar(&p.DNSServers, "dnsserver", nil, "remote dns server")
f.StringVar(&p.IPSet, "ipset", "", "ipset NAME, will create 2 sets: NAME for ipv4 and NAME6 for ipv6")
f.StringVar(&p.NFTSet, "nftset", "", "nftables set [[FAMILY/]TABLE/]NAME, defaults to FAMILY=inet and TABLE=glider; will create NAME for ipv4 and NAME6 for ipv6")
f.StringSliceVar(&p.Domain, "domain", nil, "domain")
f.StringSliceVar(&p.IP, "ip", nil, "ip")

View File

@ -1,6 +1,6 @@
[Unit]
Description=Glider Service (%i)
After=network.target iptables.service ip6tables.service
After=network.target iptables.service ip6tables.service nftables.service
[Service]
Type=simple
@ -13,7 +13,7 @@ ExecStart=/usr/bin/glider -config /etc/glider/%i.conf
# NOTE:
# work with systemd v229 or later, so glider can listen on port below 1024 with none-root user
# CAP_NET_ADMIN: ipset, setsockopt: IP_TRANSPARENT
# CAP_NET_ADMIN: ipset, nftables set, setsockopt: IP_TRANSPARENT
# CAP_NET_BIND_SERVICE: bind ports under 1024
# CAP_NET_RAW: bind raw socket and broadcasting (used by dhcpd)
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW