@@ -10,8 +10,7 @@ import (
1010 "syscall"
1111 "time"
1212
13- blockservice "github.com/Azure/azure-container-networking/bpf-prog/block-iptables/pkg/blockservice"
14- "github.com/cilium/ebpf/link"
13+ "github.com/Azure/azure-container-networking/bpf-prog/block-iptables/pkg/bpfprogram"
1514 "github.com/cilium/ebpf/rlimit"
1615 "github.com/fsnotify/fsnotify"
1716)
@@ -20,24 +19,18 @@ const (
2019 DefaultConfigFile = "/etc/cni/net.d/iptables-allow-list"
2120)
2221
23- // BPFProgram wraps the eBPF program and its links
24- type BPFProgram struct {
25- objs * blockservice.BlockIptablesObjects
26- links []link.Link
27- attached bool
22+ // BlockConfig holds configuration for the application
23+ type BlockConfig struct {
24+ ConfigFile string
25+ AttacherFactory bpfprogram.AttacherFactory
2826}
2927
30- // getHostNetnsInode gets the network namespace inode of the current process (host namespace)
31- func getHostNetnsInode () (uint32 , error ) {
32- var stat syscall.Stat_t
33- err := syscall .Stat ("/proc/self/ns/net" , & stat )
34- if err != nil {
35- return 0 , fmt .Errorf ("failed to stat /proc/self/ns/net: %w" , err )
28+ // NewDefaultBlockConfig creates a new BlockConfig with default values
29+ func NewDefaultBlockConfig () * BlockConfig {
30+ return & BlockConfig {
31+ ConfigFile : DefaultConfigFile ,
32+ AttacherFactory : bpfprogram .NewProgram ,
3633 }
37-
38- inode := uint32 (stat .Ino )
39- log .Printf ("Host network namespace inode: %d" , inode )
40- return inode , nil
4134}
4235
4336// isFileEmptyOrMissing checks if the config file exists and has content
@@ -62,113 +55,6 @@ func isFileEmptyOrMissing(filename string) int {
6255 return 0 // File exists and has content
6356}
6457
65- // attachBPFProgram attaches the BPF program to LSM hooks
66- func (bp * BPFProgram ) attachBPFProgram () error {
67- if bp .attached {
68- log .Println ("BPF program already attached" )
69- return nil
70- }
71-
72- log .Println ("Attaching BPF program..." )
73-
74- // Get the host network namespace inode
75- hostNetnsInode , err := getHostNetnsInode ()
76- if err != nil {
77- return fmt .Errorf ("failed to get host network namespace inode: %w" , err )
78- }
79-
80- // Load BPF objects with the host namespace inode set
81- spec , err := blockservice .LoadBlockIptables ()
82- if err != nil {
83- return fmt .Errorf ("failed to load BPF spec: %w" , err )
84- }
85-
86- // Set the host_netns_inode variable in the BPF program before loading
87- // Note: The C program sets it to hostNetnsInode + 1, so we do the same
88- if err := spec .RewriteConstants (map [string ]interface {}{
89- "host_netns_inode" : hostNetnsInode ,
90- }); err != nil {
91- return fmt .Errorf ("failed to rewrite constants: %w" , err )
92- }
93-
94- // Load the objects
95- objs := & blockservice.BlockIptablesObjects {}
96- if err := spec .LoadAndAssign (objs , nil ); err != nil {
97- return fmt .Errorf ("failed to load BPF objects: %w" , err )
98- }
99- bp .objs = objs
100-
101- // Attach LSM programs
102- var links []link.Link
103-
104- // Attach socket_setsockopt LSM hook
105- if bp .objs .IptablesLegacyBlock != nil {
106- l , err := link .AttachLSM (link.LSMOptions {
107- Program : bp .objs .IptablesLegacyBlock ,
108- })
109- if err != nil {
110- bp .objs .Close ()
111- return fmt .Errorf ("failed to attach iptables_legacy_block LSM: %w" , err )
112- }
113- links = append (links , l )
114- }
115-
116- // Attach netlink_send LSM hook
117- if bp .objs .IptablesNftablesBlock != nil {
118- l , err := link .AttachLSM (link.LSMOptions {
119- Program : bp .objs .IptablesNftablesBlock ,
120- })
121- if err != nil {
122- // Clean up previous links
123- for _ , link := range links {
124- link .Close ()
125- }
126- bp .objs .Close ()
127- return fmt .Errorf ("failed to attach block_nf_netlink LSM: %w" , err )
128- }
129- links = append (links , l )
130- }
131-
132- bp .links = links
133- bp .attached = true
134-
135- log .Printf ("BPF program attached successfully with host_netns_inode=%d" , hostNetnsInode )
136- return nil
137- }
138-
139- // detachBPFProgram detaches the BPF program
140- func (bp * BPFProgram ) detachBPFProgram () error {
141- if ! bp .attached {
142- log .Println ("BPF program already detached" )
143- return nil
144- }
145-
146- log .Println ("Detaching BPF program..." )
147-
148- // Close all links
149- for _ , l := range bp .links {
150- if err := l .Close (); err != nil {
151- log .Printf ("Warning: failed to close link: %v" , err )
152- }
153- }
154- bp .links = nil
155-
156- // Close objects
157- if bp .objs != nil {
158- bp .objs .Close ()
159- bp .objs = nil
160- }
161-
162- bp .attached = false
163- log .Println ("BPF program detached successfully" )
164- return nil
165- }
166-
167- // Close cleans up all resources
168- func (bp * BPFProgram ) Close () {
169- bp .detachBPFProgram ()
170- }
171-
17258// setupFileWatcher sets up a file watcher for the config file
17359func setupFileWatcher (configFile string ) (* fsnotify.Watcher , error ) {
17460 watcher , err := fsnotify .NewWatcher ()
@@ -189,7 +75,7 @@ func setupFileWatcher(configFile string) (*fsnotify.Watcher, error) {
18975}
19076
19177// handleFileEvent processes file system events
192- func handleFileEvent (event fsnotify.Event , configFile string , bp * BPFProgram ) {
78+ func handleFileEvent (event fsnotify.Event , configFile string , bp bpfprogram. Attacher ) {
19379 // Check if the event is for our config file
19480 if filepath .Base (event .Name ) != filepath .Base (configFile ) {
19581 return
@@ -205,48 +91,42 @@ func handleFileEvent(event fsnotify.Event, configFile string, bp *BPFProgram) {
20591 switch fileState {
20692 case 1 : // File is empty
20793 log .Println ("File is empty, attaching BPF program" )
208- if err := bp .attachBPFProgram (); err != nil {
94+ if err := bp .Attach (); err != nil {
20995 log .Printf ("Failed to attach BPF program: %v" , err )
21096 }
21197 case 0 : // File has content
21298 log .Println ("File has content, detaching BPF program" )
213- if err := bp .detachBPFProgram (); err != nil {
99+ if err := bp .Detach (); err != nil {
214100 log .Printf ("Failed to detach BPF program: %v" , err )
215101 }
216102 case - 1 : // File is missing
217103 log .Println ("Config file was deleted, detaching BPF program" )
218- if err := bp .detachBPFProgram (); err != nil {
104+ if err := bp .Detach (); err != nil {
219105 log .Printf ("Failed to detach BPF program: %v" , err )
220106 }
221107 }
222108}
223109
224- func main () {
225- configFile := DefaultConfigFile
226-
227- // Parse command line arguments
228- if len (os .Args ) > 1 {
229- configFile = os .Args [1 ]
230- }
231-
232- log .Printf ("Using config file: %s" , configFile )
110+ // run is the main application logic, separated for easier testing
111+ func run (config * BlockConfig ) error {
112+ log .Printf ("Using config file: %s" , config .ConfigFile )
233113
234114 // Remove memory limit for eBPF
235115 if err := rlimit .RemoveMemlock (); err != nil {
236- log . Fatalf ( "Failed to remove memlock rlimit: %v " , err )
116+ return fmt . Errorf ( "failed to remove memlock rlimit: %w " , err )
237117 }
238118
239- // Initialize BPF program wrapper
240- bp := & BPFProgram {}
119+ // Initialize BPF program attacher using the factory
120+ bp := config . AttacherFactory ()
241121 defer bp .Close ()
242122
243123 // Initial state check
244- fileState := isFileEmptyOrMissing (configFile )
124+ fileState := isFileEmptyOrMissing (config . ConfigFile )
245125 switch fileState {
246126 case 1 : // File is empty
247127 log .Println ("File is empty, attaching BPF program" )
248- if err := bp .attachBPFProgram (); err != nil {
249- log . Fatalf ( "Failed to attach BPF program: %v " , err )
128+ if err := bp .Attach (); err != nil {
129+ return fmt . Errorf ( "failed to attach BPF program: %w " , err )
250130 }
251131 case 0 : // File has content
252132 log .Println ("Config file has content, BPF program will remain detached" )
@@ -255,9 +135,9 @@ func main() {
255135 }
256136
257137 // Setup file watcher
258- watcher , err := setupFileWatcher (configFile )
138+ watcher , err := setupFileWatcher (config . ConfigFile )
259139 if err != nil {
260- log . Fatalf ( "Failed to setup file watcher: %v " , err )
140+ return fmt . Errorf ( "failed to setup file watcher: %w " , err )
261141 }
262142 defer watcher .Close ()
263143
@@ -276,25 +156,38 @@ func main() {
276156 case event , ok := <- watcher .Events :
277157 if ! ok {
278158 log .Println ("Watcher events channel closed" )
279- return
159+ return nil
280160 }
281- handleFileEvent (event , configFile , bp )
161+ handleFileEvent (event , config . ConfigFile , bp )
282162
283163 case err , ok := <- watcher .Errors :
284164 if ! ok {
285165 log .Println ("Watcher errors channel closed" )
286- return
166+ return nil
287167 }
288168 log .Printf ("Watcher error: %v" , err )
289169
290170 case sig := <- sigChan :
291171 log .Printf ("Received signal: %v" , sig )
292172 cancel ()
293- return
173+ return nil
294174
295175 case <- ctx .Done ():
296176 log .Println ("Context cancelled, exiting" )
297- return
177+ return nil
298178 }
299179 }
300180}
181+
182+ func main () {
183+ config := NewDefaultBlockConfig ()
184+
185+ // Parse command line arguments
186+ if len (os .Args ) > 1 {
187+ config .ConfigFile = os .Args [1 ]
188+ }
189+
190+ if err := run (config ); err != nil {
191+ log .Fatalf ("Application failed: %v" , err )
192+ }
193+ }
0 commit comments