44package main
55
66import (
7- "context "
7+ "flag "
88 "fmt"
99 "log"
1010 "os"
11- "os/signal"
12- "path/filepath"
13- "syscall"
14- "time"
1511
1612 "github.com/Azure/azure-container-networking/bpf-prog/azure-block-iptables/pkg/bpfprogram"
1713 "github.com/cilium/ebpf/rlimit"
18- "github.com/fsnotify/fsnotify"
1914)
2015
21- const (
22- DefaultConfigFile = "/etc/cni/net.d/iptables-allow-list"
23- )
16+ // ProgramVersion is set during build
17+ var version = "unknown"
2418
25- // BlockConfig holds configuration for the application
26- type BlockConfig struct {
27- ConfigFile string
19+ // Config holds configuration for the application
20+ type Config struct {
21+ Mode string // "attach" or "detach"
2822 AttacherFactory bpfprogram.AttacherFactory
2923}
3024
31- // NewDefaultBlockConfig creates a new BlockConfig with default values
32- func NewDefaultBlockConfig () * BlockConfig {
33- return & BlockConfig {
34- ConfigFile : DefaultConfigFile ,
35- AttacherFactory : bpfprogram . NewProgram ,
36- }
37- }
25+ // parseArgs parses command line arguments and returns the configuration
26+ func parseArgs () ( * Config , error ) {
27+ var (
28+ mode = flag . String ( "mode" , "" , "Operation mode: 'attach' or 'detach' (required)" )
29+ showVersion = flag . Bool ( "version" , false , "Show version information" )
30+ showHelp = flag . Bool ( "help" , false , "Show help information" )
31+ )
3832
39- // isFileEmptyOrMissing checks if the config file exists and has content
40- // Returns: 1 if empty, 0 if has content, -1 if missing/error
41- func isFileEmptyOrMissing (filename string ) int {
42- stat , err := os .Stat (filename )
43- if err != nil {
44- if os .IsNotExist (err ) {
45- log .Printf ("Config file %s does not exist" , filename )
46- return - 1 // File missing
47- }
48- log .Printf ("Error checking file %s: %v" , filename , err )
49- return - 1 // Treat errors as missing
50- }
33+ flag .Parse ()
5134
52- if stat . Size () == 0 {
53- log .Printf ("Config file %s is empty " , filename )
54- return 1 // File empty
35+ if * showVersion {
36+ fmt .Printf ("azure-block-iptables version %s\n " , version )
37+ os . Exit ( 0 )
5538 }
5639
57- log .Printf ("Config file %s has content (size: %d bytes)" , filename , stat .Size ())
58- return 0 // File exists and has content
59- }
60-
61- // setupFileWatcher sets up a file watcher for the config file
62- func setupFileWatcher (configFile string ) (* fsnotify.Watcher , error ) {
63- watcher , err := fsnotify .NewWatcher ()
64- if err != nil {
65- return nil , fmt .Errorf ("failed to create file watcher: %w" , err )
40+ if * showHelp {
41+ flag .PrintDefaults ()
42+ os .Exit (0 )
6643 }
6744
68- // Watch the directory containing the config file
69- dir := filepath .Dir (configFile )
70- err = watcher .Add (dir )
71- if err != nil {
72- watcher .Close ()
73- return nil , fmt .Errorf ("failed to add watch for directory %s: %w" , dir , err )
45+ if * mode == "" {
46+ return nil , fmt .Errorf ("mode is required. Use -mode=attach or -mode=detach" )
7447 }
7548
76- log .Printf ("Watching directory %s for changes to %s" , dir , configFile )
77- return watcher , nil
78- }
79-
80- func checkFileStatusAndUpdateBPF (configFile string , bp bpfprogram.Attacher ) {
81- // Check current state and take action
82- fileState := isFileEmptyOrMissing (configFile )
83- switch fileState {
84- case 1 : // File is empty
85- log .Println ("File is empty, attaching BPF program" )
86- if err := bp .Attach (); err != nil { // No-op if already attached
87- log .Printf ("Failed to attach BPF program: %v" , err )
88- }
89- case 0 : // File has content
90- log .Println ("File has content, detaching BPF program" )
91- if err := bp .Detach (); err != nil { // No-op if already detached
92- log .Printf ("Failed to detach BPF program: %v" , err )
93- }
94- case - 1 : // File is missing
95- log .Println ("Config file was deleted, detaching BPF program" )
96- if err := bp .Detach (); err != nil { // No-op if already detached
97- log .Printf ("Failed to detach BPF program: %v" , err )
98- }
99- }
100- }
101-
102- // handleFileEvent processes file system events
103- func handleFileEvent (event fsnotify.Event , configFile string , bp bpfprogram.Attacher ) {
104- // Check if the event is for our config file
105- if filepath .Base (event .Name ) != filepath .Base (configFile ) {
106- return
49+ if * mode != "attach" && * mode != "detach" {
50+ return nil , fmt .Errorf ("invalid mode '%s'. Must be 'attach' or 'detach'" , * mode )
10751 }
10852
109- log .Printf ("Config file changed: %s (operation: %s)" , event .Name , event .Op )
110-
111- // Small delay to handle rapid successive events
112- time .Sleep (100 * time .Millisecond )
113- checkFileStatusAndUpdateBPF (configFile , bp )
53+ return & Config {
54+ Mode : * mode ,
55+ AttacherFactory : bpfprogram .NewProgram ,
56+ }, nil
11457}
11558
116- // run is the main application logic, separated for easier testing
117- func run (config * BlockConfig ) error {
118- log .Printf ( "Using config file: %s" , config . ConfigFile )
59+ // attachMode handles the attach operation
60+ func attachMode (config * Config ) error {
61+ log .Println ( "Starting attach mode..." )
11962
12063 // Remove memory limit for eBPF
12164 if err := rlimit .RemoveMemlock (); err != nil {
@@ -124,62 +67,45 @@ func run(config *BlockConfig) error {
12467
12568 // Initialize BPF program attacher using the factory
12669 bp := config .AttacherFactory ()
127- defer bp .Close ()
12870
129- // Check initial state of the config file
130- checkFileStatusAndUpdateBPF (config .ConfigFile , bp )
131-
132- // Setup file watcher
133- watcher , err := setupFileWatcher (config .ConfigFile )
134- if err != nil {
135- return fmt .Errorf ("failed to setup file watcher: %w" , err )
71+ // Attach the BPF program
72+ if err := bp .Attach (); err != nil {
73+ return fmt .Errorf ("failed to attach BPF program: %w" , err )
13674 }
137- defer watcher .Close ()
138-
139- // Setup signal handling
140- ctx , cancel := context .WithCancel (context .Background ())
141- defer cancel ()
142-
143- sigChan := make (chan os.Signal , 1 )
144- signal .Notify (sigChan , syscall .SIGINT , syscall .SIGTERM )
145-
146- log .Println ("Starting file watch loop..." )
147-
148- // Main event loop
149- for {
150- select {
151- case event , ok := <- watcher .Events :
152- if ! ok {
153- log .Println ("Watcher events channel closed" )
154- return nil
155- }
156- handleFileEvent (event , config .ConfigFile , bp )
157-
158- case err , ok := <- watcher .Errors :
159- if ! ok {
160- log .Println ("Watcher errors channel closed" )
161- return nil
162- }
163- log .Printf ("Watcher error: %v" , err )
164-
165- case sig := <- sigChan :
166- log .Printf ("Received signal: %v" , sig )
167- cancel ()
168- return nil
169-
170- case <- ctx .Done ():
171- log .Println ("Context cancelled, exiting" )
172- return nil
173- }
75+
76+ log .Println ("BPF program attached successfully" )
77+ return nil
78+ }
79+
80+ // detachMode handles the detach operation
81+ func detachMode (config * Config ) error {
82+ log .Println ("Starting detach mode..." )
83+
84+ // Initialize BPF program attacher using the factory
85+ bp := config .AttacherFactory ()
86+ bp .Detach ()
87+ log .Println ("BPF program detached successfully" )
88+ return nil
89+ }
90+
91+ // run is the main application logic
92+ func run (config * Config ) error {
93+ switch config .Mode {
94+ case "attach" :
95+ return attachMode (config )
96+ case "detach" :
97+ return detachMode (config )
98+ default :
99+ return fmt .Errorf ("unsupported mode: %s" , config .Mode )
174100 }
175101}
176102
177103func main () {
178- config := NewDefaultBlockConfig ()
179-
180- // Parse command line arguments
181- if len ( os . Args ) > 1 {
182- config . ConfigFile = os .Args [ 1 ]
104+ config , err := parseArgs ()
105+ if err != nil {
106+ log . Printf ( "Error parsing arguments: %v" , err )
107+ flag . PrintDefaults ()
108+ os .Exit ( 1 )
183109 }
184110
185111 if err := run (config ); err != nil {
0 commit comments