├── .github └── workflows │ └── goreleaser.yml ├── .gitignore ├── .goreleaser.yml ├── .vscode └── launch.json ├── README.md ├── config └── config.go ├── core ├── executor.go ├── generator.go └── watcher.go ├── go.mod ├── go.sum ├── log └── log.go └── main.go /.github/workflows/goreleaser.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | jobs: 8 | goreleaser: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | with: 14 | fetch-depth: 0 15 | - name: Set up Go 16 | uses: actions/setup-go@v2 17 | with: 18 | go-version: 1.17 19 | - name: Run GoReleaser 20 | uses: goreleaser/goreleaser-action@v2 21 | with: 22 | version: latest 23 | args: release --rm-dist 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | config.yml 3 | nfg.nft -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - binary: nfg 3 | goos: 4 | - linux 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${workspaceFolder}", 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 一个简单的基于`nftables`的端口转发工具 2 | 3 | 使用方法: 4 | 5 | 1. 安装nftables 6 | ``` 7 | apt install -y nftables // Debian/Ubuntu 8 | yum install -y nftables // CentOS 9 | ``` 10 | 2. 打开IPv4转发 11 | 12 | ``` 13 | echo -e "net.ipv4.ip_forward=1" >> /etc/sysctl.conf && sysctl -p 14 | ``` 15 | 可能需要重启生效 16 | 17 | 2. 新建一个配置文件`nfg.yml` 18 | ```yml 19 | rules: 20 | - srcPort: 8081 #源端口 21 | dstAddr: 2.2.2.2 #目标地址 22 | dstPort: 8082 #目标端口 23 | protocol: both #可选 tcp udp both 24 | - srcAddr: 3.3.3.3 #源地址,如果不知道填什么就把这一项删了,程序自动获取 25 | srcPort: 8081-8089 #支持端口段 26 | dstAddr: 4.4.4.4 27 | dstPort: 8081-8089 28 | protocol: tcp 29 | ``` 30 | 31 | 3. 执行`./nfg -w -s nfg.yml`,监听配置文件的变化实时更新NAT规则,同时每隔一分钟会更新域名所对应的IP 32 | 也可执行`./nfg -g -s nfg.yml`,生成转发规则并输出到终端 -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "net/http" 7 | "net/url" 8 | "os" 9 | "strings" 10 | 11 | "github.com/go-playground/validator/v10" 12 | "gopkg.in/yaml.v3" 13 | ) 14 | 15 | type Rule struct { 16 | SrcAddr string `yaml:"srcAddr,omitempty" validate:"ip|fqdn"` 17 | SrcPort string `yaml:"srcPort,omitempty" validate:"required"` 18 | DstAddr string `yaml:"dstAddr,omitempty" validate:"ip|fqdn"` 19 | DstPort string `yaml:"dstPort,omitempty" validate:"required"` 20 | Protocol string `yaml:"protocol,omitempty" validate:"oneof=tcp udp both"` 21 | } 22 | 23 | type Config struct { 24 | Rules []Rule `yaml:"rules,omitempty" validate:"dive,required"` 25 | } 26 | 27 | func IsRemote(src string) bool { 28 | _, err := url.ParseRequestURI(src) 29 | return err == nil 30 | } 31 | 32 | func NewConfig(src string) (Config, error) { 33 | if IsRemote(src) { 34 | return NewRemoteConfig(src) 35 | } 36 | 37 | return NewLocalConfig(src) 38 | } 39 | 40 | func NewLocalConfig(src string) (Config, error) { 41 | b, err := readFromLocal(src) 42 | if err != nil { 43 | return Config{}, err 44 | } 45 | 46 | return newConfig(b) 47 | } 48 | func NewRemoteConfig(src string) (Config, error) { 49 | b, err := readFromRemote(src) 50 | if err != nil { 51 | return Config{}, err 52 | } 53 | 54 | return newConfig(b) 55 | } 56 | 57 | func newConfig(b []byte) (Config, error) { 58 | c, err := parse(b) 59 | if err != nil { 60 | return Config{}, err 61 | } 62 | 63 | err = validate(&c) 64 | if err != nil { 65 | return Config{}, err 66 | } 67 | 68 | return c, nil 69 | } 70 | 71 | func readFromLocal(path string) ([]byte, error) { 72 | f, err := os.Open(path) 73 | if err != nil { 74 | return nil, err 75 | } 76 | defer f.Close() 77 | 78 | b, err := io.ReadAll(f) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | return b, nil 84 | } 85 | 86 | func readFromRemote(url string) ([]byte, error) { 87 | resp, err := http.Get(url) 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | b, err := io.ReadAll(resp.Body) 93 | if err != nil { 94 | return nil, err 95 | } 96 | 97 | return b, nil 98 | } 99 | 100 | func parse(b []byte) (Config, error) { 101 | var c Config 102 | err := yaml.Unmarshal(b, &c) 103 | if err != nil { 104 | return Config{}, err 105 | } 106 | 107 | return c, nil 108 | } 109 | 110 | func validate(c *Config) error { 111 | addr, err := defaultSrcAddr() 112 | if err != nil { 113 | return err 114 | } 115 | 116 | for i := range c.Rules { 117 | if c.Rules[i].SrcAddr == "" { 118 | c.Rules[i].SrcAddr = addr 119 | } 120 | } 121 | 122 | return validator.New().Struct(c) 123 | } 124 | 125 | func defaultSrcAddr() (string, error) { 126 | conn, err := net.Dial("udp", "223.5.5.5:53") 127 | if err != nil { 128 | return "", err 129 | } 130 | defer conn.Close() 131 | return strings.Split(conn.LocalAddr().String(), ":")[0], nil 132 | } 133 | -------------------------------------------------------------------------------- /core/executor.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | func Apply(path string) error { 8 | return exec.Command("nft", "-f", path).Run() 9 | } 10 | -------------------------------------------------------------------------------- /core/generator.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | 9 | "github.com/kzw200015/nfg/config" 10 | ) 11 | 12 | const ( 13 | rulePrefix = `add table ip nat 14 | delete table ip nat 15 | add table ip nat 16 | add chain nat PREROUTING { type nat hook prerouting priority -100 ; } 17 | add chain nat POSTROUTING { type nat hook postrouting priority 100 ; } 18 | 19 | ` 20 | ruleFormat = `add rule ip nat PREROUTING %v dport %v counter dnat to %v:%v 21 | add rule ip nat POSTROUTING ip daddr %v %v dport %v counter snat to %v 22 | ` 23 | ) 24 | 25 | func SaveToFile(rules []config.Rule, fileName string) error { 26 | f, err := os.Create(fileName) 27 | if err != nil { 28 | return err 29 | } 30 | defer f.Close() 31 | 32 | _, err = io.WriteString(f, Generate(rules)) 33 | 34 | return err 35 | } 36 | 37 | func Generate(rules []config.Rule) string { 38 | var builder strings.Builder 39 | builder.WriteString(rulePrefix) 40 | 41 | for _, rule := range rules { 42 | switch rule.Protocol { 43 | case "tcp": 44 | builder.WriteString(genTcpRule(rule)) 45 | case "udp": 46 | builder.WriteString(genUdpRule(rule)) 47 | case "both": 48 | builder.WriteString(genTcpRule(rule)) 49 | builder.WriteString(genUdpRule(rule)) 50 | } 51 | } 52 | 53 | return builder.String() 54 | } 55 | 56 | func genTcpRule(rule config.Rule) string { 57 | return fmt.Sprintf(ruleFormat, "tcp", rule.SrcPort, rule.DstAddr, rule.DstPort, rule.DstAddr, "tcp", rule.DstPort, rule.SrcAddr) 58 | } 59 | 60 | func genUdpRule(rule config.Rule) string { 61 | return fmt.Sprintf(ruleFormat, "udp", rule.SrcPort, rule.DstAddr, rule.DstPort, rule.DstAddr, "udp", rule.DstPort, rule.SrcAddr) 62 | } 63 | -------------------------------------------------------------------------------- /core/watcher.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | 7 | "github.com/fsnotify/fsnotify" 8 | "github.com/kzw200015/nfg/config" 9 | "github.com/kzw200015/nfg/log" 10 | ) 11 | 12 | const defaultInterval = time.Duration(30) * time.Second 13 | 14 | type Watcher struct { 15 | src string 16 | temp string 17 | mu sync.Mutex 18 | } 19 | 20 | func NewWatcher(src, temp string) *Watcher { 21 | return &Watcher{src, temp, sync.Mutex{}} 22 | } 23 | 24 | func (w *Watcher) Watch() { 25 | var fsChan <-chan error 26 | if !config.IsRemote(w.src) { 27 | fsChan = w.watchFs() 28 | } 29 | 30 | schChan := w.schedule() 31 | 32 | for { 33 | select { 34 | case err := <-fsChan: 35 | log.Logger.Errorln(err) 36 | case err := <-schChan: 37 | log.Logger.Errorln(err) 38 | } 39 | } 40 | } 41 | 42 | func (w *Watcher) schedule() <-chan error { 43 | errChan := make(chan error) 44 | go func() { 45 | for { 46 | go w.do(errChan) 47 | time.Sleep(defaultInterval) 48 | } 49 | }() 50 | return errChan 51 | } 52 | 53 | func (w *Watcher) watchFs() <-chan error { 54 | errChan := make(chan error) 55 | 56 | go func() { 57 | watcher, err := fsnotify.NewWatcher() 58 | if err != nil { 59 | log.Logger.Panicln(err) 60 | } 61 | 62 | defer watcher.Close() 63 | 64 | err = watcher.Add(w.src) 65 | if err != nil { 66 | log.Logger.Panicln(err) 67 | } 68 | for { 69 | select { 70 | case event := <-watcher.Events: 71 | if event.Op == fsnotify.Remove { 72 | err = watcher.Add(w.src) 73 | if err != nil { 74 | errChan <- err 75 | } 76 | } 77 | if event.Op == fsnotify.Write || event.Op == fsnotify.Chmod { 78 | log.Logger.Infoln("检测到配置文件更改") 79 | go w.do(errChan) 80 | } 81 | case err := <-watcher.Errors: 82 | if err != nil { 83 | errChan <- err 84 | } 85 | } 86 | } 87 | }() 88 | 89 | return errChan 90 | } 91 | 92 | func (w *Watcher) do(errChan chan<- error) { 93 | w.mu.Lock() 94 | log.Logger.Infoln("读取配置文件") 95 | c, err := config.NewLocalConfig(w.src) 96 | if err != nil { 97 | errChan <- err 98 | } 99 | log.Logger.Infoln("生成转发规则") 100 | if err = SaveToFile(c.Rules, w.temp); err != nil { 101 | errChan <- err 102 | } 103 | log.Logger.Infoln("应用转发规则") 104 | if err = Apply(w.temp); err != nil { 105 | errChan <- err 106 | } 107 | w.mu.Unlock() 108 | } 109 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kzw200015/nfg 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/fsnotify/fsnotify v1.5.1 7 | github.com/go-playground/validator/v10 v10.9.0 8 | github.com/sirupsen/logrus v1.8.1 9 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= 6 | github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= 7 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 8 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 9 | github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= 10 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= 11 | github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= 12 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= 13 | github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= 14 | github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= 15 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 16 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 17 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 18 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 19 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 20 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 21 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 22 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 23 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= 24 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 25 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 26 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 27 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 28 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 29 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 30 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 31 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 32 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 33 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 34 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 35 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 36 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 37 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 38 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= 39 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 40 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 41 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 42 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 43 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 44 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 45 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= 46 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 47 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 48 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 49 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 50 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 51 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 52 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 53 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 54 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 55 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 56 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 57 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 58 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 59 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 60 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/sirupsen/logrus" 8 | ) 9 | 10 | var Logger *logrus.Logger = defaultLogger(logrus.InfoLevel) 11 | 12 | func defaultLogger(level logrus.Level) *logrus.Logger { 13 | logger := logrus.New() 14 | logger.SetReportCaller(true) 15 | logger.SetLevel(level) 16 | logger.SetFormatter(&defaultFormatter{}) 17 | return logger 18 | } 19 | 20 | type defaultFormatter struct{} 21 | 22 | func (*defaultFormatter) Format(entry *logrus.Entry) ([]byte, error) { 23 | var b bytes.Buffer 24 | t := entry.Time.Format("2006-01-02 15:04:05") 25 | s := fmt.Sprintf("%s [%s] %s\n", t, entry.Level, entry.Message) 26 | b.WriteString(s) 27 | return b.Bytes(), nil 28 | } 29 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "github.com/kzw200015/nfg/config" 8 | "github.com/kzw200015/nfg/core" 9 | "github.com/kzw200015/nfg/log" 10 | ) 11 | 12 | func main() { 13 | isWatch := flag.Bool("w", false, "Run as a watch mode") 14 | isGenerate := flag.Bool("g", false, "Generate to stdout") 15 | src := flag.String("s", "", "The source of config") 16 | temp := flag.String("t", "/tmp/nat.nft", "The rule file path") 17 | flag.Parse() 18 | 19 | if *src == "" { 20 | log.Logger.Errorln("Please input the config file source") 21 | flag.Usage() 22 | return 23 | } 24 | 25 | if *isWatch { 26 | w := core.NewWatcher(*src, *temp) 27 | w.Watch() 28 | } else if *isGenerate { 29 | c, err := config.NewConfig(*src) 30 | if err != nil { 31 | log.Logger.Panicln(err) 32 | } 33 | fmt.Print(core.Generate(c.Rules)) 34 | } else { 35 | flag.Usage() 36 | } 37 | } 38 | --------------------------------------------------------------------------------