Skip to content

Commit ce9484b

Browse files
committed
Update Go module version, enhance README command-line arguments, and refactor main command structure
1 parent 49d1ea2 commit ce9484b

26 files changed

+731
-67
lines changed

README.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,22 @@ go build -o pf ./cmd/pf
5454

5555
### Command-Line Arguments
5656
```bash
57-
Usage: pf -stream <script.psl> [-proto dir] [-iface interface] [-dry-run]
58-
-stream string
59-
Packet stream language file (required)
60-
-proto string
61-
Protocol definition directory (.pdl files) (default "proto")
62-
-iface string
63-
Network interface to send packets (e.g. eth0, lo) (default "lo")
64-
-dry-run
65-
Parse and build packets only, do not actually send them
57+
Usage:
58+
pf [flags]
59+
60+
Flags:
61+
--builtin-proto Load built-in common protocols first (eth/vlan/arp/arp_request/arp_reply/ip/ipv6/icmp/icmp6/ndp_ns/ndp_na/udp/tcp) (default true)
62+
--dry-run Parse and build packets only, do not actually send
63+
-h, --help help for pf
64+
--iface string Network interface to send packets (e.g. eth0, lo) (default "lo")
65+
--proto string Protocol definition directory (.pdl files), optional (default "proto")
66+
--stream string Packet stream language file (required)
67+
-v, --version version for pf
68+
```
69+
70+
Show builtin protocol list:
71+
```bash
72+
pf builtin
6673
```
6774

6875
### Quick Example

build.sh

100644100755
File mode changed.

cmd/pf/main.go

Lines changed: 78 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
package main
33

44
import (
5-
"flag"
6-
"strings"
75
"fmt"
86
"os"
9-
"path/filepath"
107
"runtime"
8+
"strings"
9+
10+
"github.com/spf13/cobra"
11+
"github.com/spf13/viper"
1112
"github.com/yanjiulab/packetforge/pkg/engine"
1213
"github.com/yanjiulab/packetforge/pkg/packet"
1314
"github.com/yanjiulab/packetforge/pkg/pdl"
@@ -92,68 +93,112 @@ func VersionString() string {
9293
)
9394
}
9495

95-
9696
func main() {
97-
pdlDir := flag.String("proto", "proto", "Protocol definition directory (.pdl files)")
98-
pslFile := flag.String("stream", "", "Packet stream language file (required)")
99-
iface := flag.String("iface", "lo", "Network interface to send packets (e.g. eth0, lo)")
100-
dryRun := flag.Bool("dry-run", false, "Parse and build packets only, do not actually send")
101-
printVersion := flag.Bool("version", false, "print version and exit")
102-
flag.Parse()
103-
104-
if *printVersion {
105-
fmt.Println(VersionString())
106-
return
97+
rootCmd := newRootCmd()
98+
if err := rootCmd.Execute(); err != nil {
99+
fmt.Fprintln(os.Stderr, err)
100+
os.Exit(1)
107101
}
102+
}
108103

109-
if *pslFile == "" {
110-
fmt.Fprintf(os.Stderr, "Usage: %s -stream <script.psl> [-proto dir] [-iface interface] [-dry-run]\n", filepath.Base(os.Args[0]))
111-
flag.Usage()
112-
os.Exit(1)
104+
func newRootCmd() *cobra.Command {
105+
rootCmd := &cobra.Command{
106+
Use: "pf",
107+
Short: "PacketForge protocol registry and packet sender",
108+
RunE: func(cmd *cobra.Command, args []string) error {
109+
pslFile := viper.GetString("stream")
110+
if pslFile == "" {
111+
return fmt.Errorf("required flag \"stream\" not set")
112+
}
113+
114+
return run(pslFile, viper.GetString("proto"), viper.GetString("iface"), viper.GetBool("dry-run"), viper.GetBool("builtin-proto"))
115+
},
113116
}
114117

118+
rootCmd.Version = VersionString()
119+
rootCmd.SetVersionTemplate("{{.Version}}\n")
120+
121+
flags := rootCmd.Flags()
122+
flags.StringP("proto", "p", "proto", "Protocol definition directory (.pdl files), optional")
123+
flags.StringP("stream", "s", "", "Packet stream language file (required)")
124+
flags.StringP("iface", "i", "lo", "Network interface to send packets (e.g. eth0, lo)")
125+
flags.BoolP("dry-run", "d", false, "Parse and build packets only, do not actually send")
126+
flags.BoolP("builtin-proto", "b", true, "Load built-in common protocols first (eth/vlan/arp/arp_request/arp_reply/ip/ipv6/icmp/icmp6/ndp_ns/ndp_na/udp/tcp)")
127+
128+
_ = viper.BindPFlag("proto", flags.Lookup("proto"))
129+
_ = viper.BindPFlag("stream", flags.Lookup("stream"))
130+
_ = viper.BindPFlag("iface", flags.Lookup("iface"))
131+
_ = viper.BindPFlag("dry-run", flags.Lookup("dry-run"))
132+
_ = viper.BindPFlag("builtin-proto", flags.Lookup("builtin-proto"))
133+
viper.SetEnvPrefix("PF")
134+
viper.AutomaticEnv()
135+
rootCmd.AddCommand(newBuiltinCmd())
136+
137+
return rootCmd
138+
}
139+
140+
func newBuiltinCmd() *cobra.Command {
141+
return &cobra.Command{
142+
Use: "builtin",
143+
Short: "Show builtin protocol list",
144+
Run: func(cmd *cobra.Command, args []string) {
145+
for _, name := range pdl.BuiltinCommonProtocolNames() {
146+
fmt.Println(name)
147+
}
148+
},
149+
}
150+
}
151+
152+
func run(pslFile, pdlDir, iface string, dryRun bool, builtinProto bool) error {
115153
// 1. Load PDL protocols
116154
reg := pdl.NewRegistry()
117-
if err := reg.LoadPDLDir(*pdlDir); err != nil {
118-
fmt.Fprintf(os.Stderr, "Load PDL: %v\n", err)
119-
os.Exit(1)
155+
if builtinProto {
156+
if err := reg.LoadBuiltinCommonProtocols(); err != nil {
157+
return fmt.Errorf("load builtin protocols: %w", err)
158+
}
159+
}
160+
if pdlDir != "" {
161+
if _, err := os.Stat(pdlDir); err == nil {
162+
if err := reg.LoadPDLDir(pdlDir); err != nil {
163+
return fmt.Errorf("load PDL dir: %w", err)
164+
}
165+
} else if !os.IsNotExist(err) || pdlDir != "proto" {
166+
return fmt.Errorf("read proto dir %q: %w", pdlDir, err)
167+
}
120168
}
121169

122170
// 2. Parse PSL script
123-
pslData, err := os.ReadFile(*pslFile)
171+
pslData, err := os.ReadFile(pslFile)
124172
if err != nil {
125-
fmt.Fprintf(os.Stderr, "Read PSL: %v\n", err)
126-
os.Exit(1)
173+
return fmt.Errorf("read PSL: %w", err)
127174
}
128175
parser := psl.NewParser(string(pslData))
129176
script, err := parser.ParseScript()
130177
if err != nil {
131-
fmt.Fprintf(os.Stderr, "Parse PSL: %v\n", err)
132-
os.Exit(1)
178+
return fmt.Errorf("parse PSL: %w", err)
133179
}
134180

135181
builder := packet.NewBuilder(reg)
136182

137183
sendFn := func(data []byte) error {
138-
if *dryRun {
184+
if dryRun {
139185
fmt.Printf("[dry-run] Send %d bytes:\n%s\n", len(data), FormatTCPDump(data, 0))
140186
return nil
141187
}
142188
return nil
143189
}
144190

145-
if !*dryRun {
146-
sender, err := packet.NewSender(*iface)
191+
if !dryRun {
192+
sender, err := packet.NewSender(iface)
147193
if err != nil {
148-
fmt.Fprintf(os.Stderr, "Create sender: %v\n", err)
149-
os.Exit(1)
194+
return fmt.Errorf("create sender: %w", err)
150195
}
151196
defer sender.Close()
152197
sendFn = sender.Send
153198
}
154199

155200
if err := engine.Run(script, builder, sendFn); err != nil {
156-
fmt.Fprintf(os.Stderr, "Run: %v\n", err)
157-
os.Exit(1)
201+
return fmt.Errorf("run: %w", err)
158202
}
203+
return nil
159204
}

examples/arp-request-reply.psl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const A_MAC = 02:00:00:00:00:0a
2+
const A_IP = 192.168.10.10
3+
const B_MAC = 02:00:00:00:00:0b
4+
const B_IP = 192.168.10.20
5+
6+
[
7+
eth(src=A_MAC, dst=ff:ff:ff:ff:ff:ff, type=0x0806)
8+
arp_request(sha=A_MAC, spa=A_IP, tha=00:00:00:00:00:00, tpa=B_IP)
9+
]
10+
11+
[
12+
eth(src=B_MAC, dst=A_MAC, type=0x0806)
13+
arp_reply(sha=B_MAC, spa=B_IP, tha=A_MAC, tpa=A_IP)
14+
]

examples/icmp6-echo.psl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const A_MAC = 02:00:00:00:00:11
2+
const B_MAC = 02:00:00:00:00:22
3+
const A_IP6 = fe80::1
4+
const B_IP6 = fe80::2
5+
6+
[
7+
eth(src=A_MAC, dst=B_MAC, type=0x86dd)
8+
ipv6(src=A_IP6, dst=B_IP6, next_header=58, hop_limit=64)
9+
icmp6(type=128, code=0, data32=0x12340001)
10+
]
11+
12+
[
13+
eth(src=B_MAC, dst=A_MAC, type=0x86dd)
14+
ipv6(src=B_IP6, dst=A_IP6, next_header=58, hop_limit=64)
15+
icmp6(type=129, code=0, data32=0x12340001)
16+
]

examples/ndp-ns-na.psl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const A_MAC = 02:00:00:00:00:11
2+
const B_MAC = 02:00:00:00:00:22
3+
const A_IP6 = fe80::1
4+
const B_IP6 = fe80::2
5+
6+
// A -> all-nodes solicited multicast: who has B_IP6
7+
[
8+
eth(src=A_MAC, dst=33:33:ff:00:00:02, type=0x86dd)
9+
ipv6(src=A_IP6, dst=ff02::1:ff00:2, next_header=58, hop_limit=255)
10+
ndp_ns(target=B_IP6, opt_slla=A_MAC)
11+
]
12+
13+
// B -> A unicast reply: B_IP6 is at B_MAC
14+
[
15+
eth(src=B_MAC, dst=A_MAC, type=0x86dd)
16+
ipv6(src=B_IP6, dst=A_IP6, next_header=58, hop_limit=255)
17+
ndp_na(target=B_IP6, opt_tlla=B_MAC)
18+
]

examples/vxlan-udp.psl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const OUTER_SRC_MAC = 02:00:00:00:10:01
2+
const OUTER_DST_MAC = 02:00:00:00:10:02
3+
const OUTER_SRC_IP = 10.10.10.1
4+
const OUTER_DST_IP = 10.10.10.2
5+
6+
const INNER_SRC_MAC = 02:00:00:00:20:01
7+
const INNER_DST_MAC = 02:00:00:00:20:02
8+
const INNER_SRC_IP = 192.168.100.1
9+
const INNER_DST_IP = 192.168.100.2
10+
11+
[
12+
// Outer tunnel headers
13+
eth(src=OUTER_SRC_MAC, dst=OUTER_DST_MAC, type=0x0800)
14+
ip(src=OUTER_SRC_IP, dst=OUTER_DST_IP, protocol=17)
15+
udp(sport=55000, dport=4789)
16+
vxlan(vni_b1=0x00, vni_b2=0x00, vni_b3=0x64)
17+
18+
// Inner packet carried by VXLAN (a UDP packet)
19+
eth(src=INNER_SRC_MAC, dst=INNER_DST_MAC, type=0x0800)
20+
ip(src=INNER_SRC_IP, dst=INNER_DST_IP, protocol=17)
21+
udp(sport=12345, dport=9000)
22+
`hello-from-inner-udp`
23+
]

go.mod

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
11
module github.com/yanjiulab/packetforge
22

3-
go 1.21
3+
go 1.23.0
4+
5+
toolchain go1.23.5
6+
7+
require (
8+
github.com/spf13/cobra v1.10.2
9+
github.com/spf13/viper v1.21.0
10+
)
11+
12+
require (
13+
github.com/fsnotify/fsnotify v1.9.0 // indirect
14+
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
15+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
16+
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
17+
github.com/sagikazarmark/locafero v0.11.0 // indirect
18+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
19+
github.com/spf13/afero v1.15.0 // indirect
20+
github.com/spf13/cast v1.10.0 // indirect
21+
github.com/spf13/pflag v1.0.10 // indirect
22+
github.com/subosito/gotenv v1.6.0 // indirect
23+
go.yaml.in/yaml/v3 v3.0.4 // indirect
24+
golang.org/x/sys v0.29.0 // indirect
25+
golang.org/x/text v0.28.0 // indirect
26+
)

go.sum

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
5+
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
6+
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
7+
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
8+
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
9+
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
10+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
11+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
12+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
13+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
14+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
15+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
16+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
17+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
18+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
19+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
20+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
21+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
22+
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
23+
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
24+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
25+
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
26+
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
27+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
28+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
29+
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
30+
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
31+
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
32+
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
33+
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
34+
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
35+
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
36+
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
37+
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
38+
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
39+
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
40+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
41+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
42+
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
43+
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
44+
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
45+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
46+
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
47+
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
48+
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
49+
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
50+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
51+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
52+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
53+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
54+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)