diff --git a/apis/v1alpha1/bpfApplicationState_types.go b/apis/v1alpha1/bpfApplicationState_types.go index 111f3515c..0ccc4f96b 100644 --- a/apis/v1alpha1/bpfApplicationState_types.go +++ b/apis/v1alpha1/bpfApplicationState_types.go @@ -25,21 +25,19 @@ import ( // BpfApplicationProgramState defines the desired state of BpfApplication // +union // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'XDP' ? has(self.xdp) : !has(self.xdp)",message="xdp configuration is required when type is XDP, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TC' ? has(self.tc) : !has(self.tc)",message="tc configuration is required when type is TC, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TC' ? has(self.tc) : !has(self.tc)",message="tc configuration is required when type is TC, and forbidden otherwise" // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TCX' ? has(self.tcx) : !has(self.tcx)",message="tcx configuration is required when type is TCX, and forbidden otherwise" // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Fentry' ? has(self.fentry) : !has(self.fentry)",message="fentry configuration is required when type is Fentry, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Fexit' ? has(self.fexit) : !has(self.fexit)",message="fexit configuration is required when type is Fexit, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Kprobe' ? has(self.kprobe) : !has(self.kprobe)",message="kprobe configuration is required when type is Kprobe, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Kretprobe' ? has(self.kretprobe) : !has(self.kretprobe)",message="kretprobe configuration is required when type is Kretprobe, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uretprobe' ? has(self.uretprobe) : !has(self.uretprobe)",message="uretprobe configuration is required when type is Uretprobe, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Tracepoint' ? has(self.tracepoint) : !has(self.tracepoint)",message="tracepoint configuration is required when type is Tracepoint, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Fexit' ? has(self.fexit) : !has(self.fexit)",message="fexit configuration is required when type is Fexit, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Kprobe' ? has(self.kprobe) : !has(self.kprobe)",message="kprobe configuration is required when type is Kprobe, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Tracepoint' ? has(self.tracepoint) : !has(self.tracepoint)",message="tracepoint configuration is required when type is Tracepoint, and forbidden otherwise" type BpfApplicationProgramState struct { BpfProgramStateCommon `json:",inline"` // Type specifies the bpf program type // +unionDiscriminator // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum:="XDP";"TC";"TCX";"Fentry";"Fexit";"Kprobe";"Kretprobe";"Uprobe";"Uretprobe";"Tracepoint" + // +kubebuilder:validation:Enum:="XDP";"TC";"TCX";"Fentry";"Fexit";"Kprobe";"Uprobe";"Tracepoint" Type EBPFProgType `json:"type,omitempty"` // xdp defines the desired state of the application's XdpPrograms. @@ -62,35 +60,25 @@ type BpfApplicationProgramState struct { // +optional Fentry *FentryProgramInfoState `json:"fentry,omitempty"` - // // fexit defines the desired state of the application's FexitPrograms. - // // +unionMember - // // +optional - // Fexit *FexitProgramInfoState `json:"fexit,omitempty"` - - // // kprobe defines the desired state of the application's KprobePrograms. - // // +unionMember - // // +optional - // Kprobe *KprobeProgramInfoState `json:"kprobe,omitempty"` - - // // kretprobe defines the desired state of the application's KretprobePrograms. - // // +unionMember - // // +optional - // Kretprobe *KprobeProgramInfoState `json:"kretprobe,omitempty"` - - // // uprobe defines the desired state of the application's UprobePrograms. - // // +unionMember - // // +optional - // Uprobe *UprobeProgramInfoState `json:"uprobe,omitempty"` - - // // uretprobe defines the desired state of the application's UretprobePrograms. - // // +unionMember - // // +optional - // Uretprobe *UprobeProgramInfoState `json:"uretprobe,omitempty"` - - // // tracepoint defines the desired state of the application's TracepointPrograms. - // // +unionMember - // // +optional - // Tracepoint *TracepointProgramInfoState `json:"tracepoint,omitempty"` + // fexit defines the desired state of the application's FexitPrograms. + // +unionMember + // +optional + Fexit *FexitProgramInfoState `json:"fexit,omitempty"` + + // kprobe defines the desired state of the application's KprobePrograms. + // +unionMember + // +optional + Kprobe *KprobeProgramInfoState `json:"kprobe,omitempty"` + + // uprobe defines the desired state of the application's UprobePrograms. + // +unionMember + // +optional + Uprobe *UprobeProgramInfoState `json:"uprobe,omitempty"` + + // tracepoint defines the desired state of the application's TracepointPrograms. + // +unionMember + // +optional + Tracepoint *TracepointProgramInfoState `json:"tracepoint,omitempty"` } // BpfApplicationSpec defines the desired state of BpfApplication @@ -118,7 +106,6 @@ type BpfApplicationStateSpec struct { // +kubebuilder:resource:scope=Cluster // BpfApplicationState contains the per-node state of a BpfApplication. -// ANF-TODO: I can't get the Node to display in the kubectl output. // +kubebuilder:printcolumn:name="Node",type=string,JSONPath=".spec.node" // +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[0].reason` // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" diff --git a/apis/v1alpha1/bpfApplication_types.go b/apis/v1alpha1/bpfApplication_types.go index 1091ae0dd..88b4c829c 100644 --- a/apis/v1alpha1/bpfApplication_types.go +++ b/apis/v1alpha1/bpfApplication_types.go @@ -42,15 +42,9 @@ const ( // ProgTypeKprobe refers to the Kprobe program type. ProgTypeKprobe EBPFProgType = "Kprobe" - // ProgTypeKretprobe refers to the Kprobe program type. - ProgTypeKretprobe EBPFProgType = "Kretprobe" - // ProgTypeUprobe refers to the Uprobe program type. ProgTypeUprobe EBPFProgType = "Uprobe" - // ProgTypeUretprobe refers to the Uretprobe program type. - ProgTypeUretprobe EBPFProgType = "Uretprobe" - // ProgTypeTracepoint refers to the Tracepoint program type. ProgTypeTracepoint EBPFProgType = "Tracepoint" ) @@ -62,17 +56,14 @@ const ( // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TCX' ? has(self.tcx) : !has(self.tcx)",message="tcx configuration is required when type is TCX, and forbidden otherwise" // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Fentry' ? has(self.fentry) : !has(self.fentry)",message="fentry configuration is required when type is Fentry, and forbidden otherwise" // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Fexit' ? has(self.fexit) : !has(self.fexit)",message="fexit configuration is required when type is Fexit, and forbidden otherwise" -// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Kprobe' ? has(self.kprobe) : !has(self.kprobe)",message="kprobe configuration is required when type is Kprobe, and forbidden otherwise" -// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Kretprobe' ? has(self.kretprobe) : !has(self.kretprobe)",message="kretprobe configuration is required when type is Kretprobe, and forbidden otherwise" // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" -// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uretprobe' ? has(self.uretprobe) : !has(self.uretprobe)",message="uretprobe configuration is required when type is Uretprobe, and forbidden otherwise" // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Tracepoint' ? has(self.tracepoint) : !has(self.tracepoint)",message="tracepoint configuration is required when type is Tracepoint, and forbidden otherwise" type BpfApplicationProgram struct { BpfProgramCommon `json:",inline"` // Type specifies the bpf program type // +unionDiscriminator // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum:="XDP";"TC";"TCX";"Fentry";"Fexit";"Kprobe";"Kretprobe";"Uprobe";"Uretprobe";"Tracepoint" + // +kubebuilder:validation:Enum:="XDP";"TC";"TCX";"Fentry";"Fexit";"Kprobe";"Uprobe";"Tracepoint" Type EBPFProgType `json:"type,omitempty"` // xdp defines the desired state of the application's XdpPrograms. @@ -105,21 +96,11 @@ type BpfApplicationProgram struct { // +optional Kprobe *KprobeProgramInfo `json:"kprobe,omitempty"` - // kretprobe defines the desired state of the application's KretprobePrograms. - // +unionMember - // +optional - Kretprobe *KprobeProgramInfo `json:"kretprobe,omitempty"` - // uprobe defines the desired state of the application's UprobePrograms. // +unionMember // +optional Uprobe *UprobeProgramInfo `json:"uprobe,omitempty"` - // uretprobe defines the desired state of the application's UretprobePrograms. - // +unionMember - // +optional - Uretprobe *UprobeProgramInfo `json:"uretprobe,omitempty"` - // tracepoint defines the desired state of the application's TracepointPrograms. // +unionMember // +optional @@ -137,9 +118,9 @@ type BpfApplicationSpec struct { // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // BpfApplication is the Schema for the bpfapplications API // +kubebuilder:printcolumn:name="NodeSelector",type=string,JSONPath=`.spec.nodeselector` diff --git a/apis/v1alpha1/bpfNsApplicationState_types.go b/apis/v1alpha1/bpfNsApplicationState_types.go index 0c32364f7..49db50545 100644 --- a/apis/v1alpha1/bpfNsApplicationState_types.go +++ b/apis/v1alpha1/bpfNsApplicationState_types.go @@ -25,16 +25,15 @@ import ( // BpfNsApplicationProgramState defines the desired state of BpfNsApplication // +union // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'XDP' ? has(self.xdp) : !has(self.xdp)",message="xdp configuration is required when type is XDP, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TC' ? has(self.tc) : !has(self.tc)",message="tc configuration is required when type is TC, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TC' ? has(self.tc) : !has(self.tc)",message="tc configuration is required when type is TC, and forbidden otherwise" // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TCX' ? has(self.tcx) : !has(self.tcx)",message="tcx configuration is required when type is TCX, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" -// // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uretprobe' ? has(self.uretprobe) : !has(self.uretprobe)",message="uretprobe configuration is required when type is Uretprobe, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" type BpfNsApplicationProgramState struct { BpfProgramStateCommon `json:",inline"` // Type specifies the bpf program type // +unionDiscriminator // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum:="XDP";"TC";"TCX";"Fentry";"Fexit";"Kprobe";"Kretprobe";"Uprobe";"Uretprobe";"Tracepoint" + // +kubebuilder:validation:Enum:="XDP";"TC";"TCX";"Uprobe" Type EBPFProgType `json:"type,omitempty"` // xdp defines the desired state of the application's XdpPrograms. @@ -42,25 +41,20 @@ type BpfNsApplicationProgramState struct { // +optional XDP *XdpNsProgramInfoState `json:"xdp,omitempty"` - // // tc defines the desired state of the application's TcPrograms. - // // +unionMember - // // +optional - // TC *TcProgramInfoState `json:"tc,omitempty"` + // tc defines the desired state of the application's TcPrograms. + // +unionMember + // +optional + TC *TcNsProgramInfoState `json:"tc,omitempty"` // tcx defines the desired state of the application's TcxPrograms. // +unionMember // +optional TCX *TcxNsProgramInfoState `json:"tcx,omitempty"` - // // uprobe defines the desired state of the application's UprobePrograms. - // // +unionMember - // // +optional - // Uprobe *UprobeProgramInfoState `json:"uprobe,omitempty"` - - // // uretprobe defines the desired state of the application's UretprobePrograms. - // // +unionMember - // // +optional - // Uretprobe *UprobeProgramInfoState `json:"uretprobe,omitempty"` + // uprobe defines the desired state of the application's UprobePrograms. + // +unionMember + // +optional + Uprobe *UprobeNsProgramInfoState `json:"uprobe,omitempty"` } // BpfNsApplicationSpec defines the desired state of BpfNsApplication diff --git a/apis/v1alpha1/bpfNsApplication_types.go b/apis/v1alpha1/bpfNsApplication_types.go index e32f1acb0..5e3fb8f19 100644 --- a/apis/v1alpha1/bpfNsApplication_types.go +++ b/apis/v1alpha1/bpfNsApplication_types.go @@ -26,13 +26,12 @@ import ( // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TC' ? has(self.tc) : !has(self.tc)",message="tc configuration is required when type is TC, and forbidden otherwise" // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TCX' ? has(self.tcx) : !has(self.tcx)",message="tcx configuration is required when type is TCX, and forbidden otherwise" // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" -// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uretprobe' ? has(self.uretprobe) : !has(self.uretprobe)",message="uretprobe configuration is required when type is Uretprobe, and forbidden otherwise" type BpfNsApplicationProgram struct { BpfProgramCommon `json:",inline"` // Type specifies the bpf program type // +unionDiscriminator // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum:="XDP";"TC";"TCX";"Uprobe";"Uretprobe" + // +kubebuilder:validation:Enum:="XDP";"TC";"TCX";"Uprobe" Type EBPFProgType `json:"type,omitempty"` // xdp defines the desired state of the application's XdpNsPrograms. @@ -54,11 +53,6 @@ type BpfNsApplicationProgram struct { // +unionMember // +optional Uprobe *UprobeNsProgramInfo `json:"uprobe,omitempty"` - - // uretprobe defines the desired state of the application's UretprobeNsPrograms. - // +unionMember - // +optional - Uretprobe *UprobeNsProgramInfo `json:"uretprobe,omitempty"` } // BpfApplicationSpec defines the desired state of BpfApplication @@ -73,9 +67,9 @@ type BpfNsApplicationSpec struct { } // +genclient -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Namespaced +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced // BpfNsApplication is the Schema for the bpfapplications API // +kubebuilder:printcolumn:name="NodeSelector",type=string,JSONPath=`.spec.nodeselector` diff --git a/apis/v1alpha1/bpfNsProgram_types.go b/apis/v1alpha1/bpfNsProgram_types.go index 58688fa3d..59ae98ccd 100644 --- a/apis/v1alpha1/bpfNsProgram_types.go +++ b/apis/v1alpha1/bpfNsProgram_types.go @@ -25,8 +25,8 @@ import ( ) // +genclient -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status // BpfNsProgram is the Schema for the Bpfnsprograms API // +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type` @@ -41,7 +41,7 @@ type BpfNsProgram struct { Status BpfProgramStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // BpfNsProgramList contains a list of BpfProgram type BpfNsProgramList struct { diff --git a/apis/v1alpha1/bpfProgram_types.go b/apis/v1alpha1/bpfProgram_types.go index 9e841bd44..8fbb9a670 100644 --- a/apis/v1alpha1/bpfProgram_types.go +++ b/apis/v1alpha1/bpfProgram_types.go @@ -26,9 +26,9 @@ import ( // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // BpfProgram is the Schema for the Bpfprograms API // +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type` @@ -63,7 +63,7 @@ type BpfProgramStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // BpfProgramList contains a list of BpfProgram type BpfProgramList struct { diff --git a/apis/v1alpha1/fentryProgram_types.go b/apis/v1alpha1/fentryProgram_types.go index 76fb3ccea..8fddfbd8a 100644 --- a/apis/v1alpha1/fentryProgram_types.go +++ b/apis/v1alpha1/fentryProgram_types.go @@ -24,9 +24,9 @@ import ( // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // FentryProgram is the Schema for the FentryPrograms API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -84,6 +84,6 @@ type FentryProgramInfoState struct { } type FentryAttachInfoState struct { - AttachInfoCommon `json:",inline"` - Attach bool `json:"attach"` + AttachInfoStateCommon `json:",inline"` + Attach bool `json:"attach"` } diff --git a/apis/v1alpha1/fexitProgram_types.go b/apis/v1alpha1/fexitProgram_types.go index 87d0f5a29..bad9d890c 100644 --- a/apis/v1alpha1/fexitProgram_types.go +++ b/apis/v1alpha1/fexitProgram_types.go @@ -24,9 +24,9 @@ import ( // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // FexitProgram is the Schema for the FexitPrograms API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -76,3 +76,13 @@ type FexitProgramList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []FexitProgram `json:"items"` } + +type FexitProgramInfoState struct { + FexitLoadInfo `json:",inline"` + FexitAttachInfoState `json:",inline"` +} + +type FexitAttachInfoState struct { + AttachInfoStateCommon `json:",inline"` + Attach bool `json:"attach"` +} diff --git a/apis/v1alpha1/kprobeProgram_types.go b/apis/v1alpha1/kprobeProgram_types.go index d53b202e0..1f5ad2bc0 100644 --- a/apis/v1alpha1/kprobeProgram_types.go +++ b/apis/v1alpha1/kprobeProgram_types.go @@ -24,9 +24,9 @@ import ( // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // KprobeProgram is the Schema for the KprobePrograms API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -63,7 +63,7 @@ type KprobeProgramInfo struct { // +kubebuilder:validation:XValidation:message="offset cannot be set for kretprobes",rule="self.retprobe == false || self.offset == 0" type KprobeAttachInfo struct { - // Functions to attach the kprobe to. + // Function to attach the kprobe to. FunctionName string `json:"func_name"` // Offset added to the address of the function for kprobe. @@ -85,3 +85,24 @@ type KprobeProgramList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []KprobeProgram `json:"items"` } + +type KprobeProgramInfoState struct { + // The list of points to which the program should be attached. The list is + // optional and may be udated after the bpf program has been loaded. + // +optional + AttachPoints []KprobeAttachInfoState `json:"attach_points"` +} + +type KprobeAttachInfoState struct { + AttachInfoStateCommon `json:",inline"` + + // Function to attach the kprobe to. + FunctionName string `json:"func_name"` + + // Offset added to the address of the function for kprobe. + // Not allowed for kretprobes. + Offset uint64 `json:"offset"` + + // Whether the program is a kretprobe. + RetProbe bool `json:"retprobe"` +} diff --git a/apis/v1alpha1/shared_types.go b/apis/v1alpha1/shared_types.go index 43a573501..c81fde3d3 100644 --- a/apis/v1alpha1/shared_types.go +++ b/apis/v1alpha1/shared_types.go @@ -127,9 +127,9 @@ type BpfAppStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` } -// AttachInfoCommon reflects the status for one attach point for a given bpf +// AttachInfoStateCommon reflects the status for one attach point for a given bpf // application program -type AttachInfoCommon struct { +type AttachInfoStateCommon struct { // ShouldAttach reflects whether the attachment should exist. ShouldAttach bool `json:"should_attach"` // ANF-TODO: Putting a uuid here for now to maintain compatibility with the diff --git a/apis/v1alpha1/tcNsProgram_types.go b/apis/v1alpha1/tcNsProgram_types.go index 6745d2e1a..164d0c0bd 100644 --- a/apis/v1alpha1/tcNsProgram_types.go +++ b/apis/v1alpha1/tcNsProgram_types.go @@ -23,9 +23,9 @@ import ( ) // +genclient -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Namespaced +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced // TcNsProgram is the Schema for the TcNsProgram API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -67,6 +67,11 @@ type TcNsAttachInfo struct { // program. Containers ContainerNsSelector `json:"containers"` + // Direction specifies the direction of traffic the tc program should + // attach to for a given network device. + // +kubebuilder:validation:Enum=ingress;egress + Direction string `json:"direction"` + // Priority specifies the priority of the tc program in relation to // other programs of the same type with the same attach point. It is a value // from 0 to 1000 where lower values have higher precedence. @@ -74,11 +79,6 @@ type TcNsAttachInfo struct { // +kubebuilder:validation:Maximum=1000 Priority int32 `json:"priority"` - // Direction specifies the direction of traffic the tc program should - // attach to for a given network device. - // +kubebuilder:validation:Enum=ingress;egress - Direction string `json:"direction"` - // ProceedOn allows the user to call other tc programs in chain on this exit code. // Multiple values are supported by repeating the parameter. // +optional @@ -94,3 +94,40 @@ type TcNsProgramList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []TcNsProgram `json:"items"` } + +type TcNsProgramInfoState struct { + // The list of points to which the program should be attached. + // TcNsAttachInfoState is similar to TcAttachInfo, but the interface and + // container selectors are expanded, and we have one instance of + // TcNsAttachInfoState for each unique attach point. The list is optional and + // may be udated after the bpf program has been loaded. + // +optional + AttachPoints []TcNsAttachInfoState `json:"attach_points"` +} + +type TcNsAttachInfoState struct { + AttachInfoStateCommon `json:",inline"` + + // Interface name to attach the tc program to. + IfName string `json:"ifname"` + + // Container pid to attach the tc program in. + ContainerPid int32 `json:"containerpid"` + + // Direction specifies the direction of traffic the tc program should + // attach to for a given network device. + // +kubebuilder:validation:Enum=ingress;egress + Direction string `json:"direction"` + + // Priority specifies the priority of the tc program in relation to + // other programs of the same type with the same attach point. It is a value + // from 0 to 1000 where lower values have higher precedence. + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=1000 + Priority int32 `json:"priority"` + + // ProceedOn allows the user to call other tc programs in chain on this exit code. + // Multiple values are supported by repeating the parameter. + // +kubebuilder:validation:MaxItems=11 + ProceedOn []TcProceedOnValue `json:"proceedon"` +} diff --git a/apis/v1alpha1/tcProgram_types.go b/apis/v1alpha1/tcProgram_types.go index 7d3f00926..b82556ff3 100644 --- a/apis/v1alpha1/tcProgram_types.go +++ b/apis/v1alpha1/tcProgram_types.go @@ -24,9 +24,9 @@ import ( // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // TcProgram is the Schema for the TcProgram API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -40,8 +40,9 @@ type TcProgram struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec TcProgramSpec `json:"spec"` - Status BpfAppStatus `json:"status,omitempty"` + Spec TcProgramSpec `json:"spec"` + // +optional + Status BpfAppStatus `json:"status,omitempty"` } // +kubebuilder:validation:Enum=unspec;ok;reclassify;shot;pipe;stolen;queued;repeat;redirect;trap;dispatcher_return @@ -72,6 +73,11 @@ type TcAttachInfo struct { // +optional Containers *ContainerSelector `json:"containers"` + // Direction specifies the direction of traffic the tc program should + // attach to for a given network device. + // +kubebuilder:validation:Enum=ingress;egress + Direction string `json:"direction"` + // Priority specifies the priority of the tc program in relation to // other programs of the same type with the same attach point. It is a value // from 0 to 1000 where lower values have higher precedence. @@ -79,11 +85,6 @@ type TcAttachInfo struct { // +kubebuilder:validation:Maximum=1000 Priority int32 `json:"priority"` - // Direction specifies the direction of traffic the tc program should - // attach to for a given network device. - // +kubebuilder:validation:Enum=ingress;egress - Direction string `json:"direction"` - // ProceedOn allows the user to call other tc programs in chain on this exit code. // Multiple values are supported by repeating the parameter. // +optional @@ -111,18 +112,19 @@ type TcProgramInfoState struct { } type TcAttachInfoState struct { - AttachInfoCommon `json:",inline"` - // An identifier for the attach point assigned by bpfman. This field is - // empty until the program is successfully attached and bpfman returns the - // id. - AttachId *uint32 `json:"attachid"` + AttachInfoStateCommon `json:",inline"` // Interface name to attach the tc program to. IfName string `json:"ifname"` // Optional container pid to attach the tc program in. // +optional - ContainerPid *uint32 `json:"containerpid"` + ContainerPid *int32 `json:"containerpid"` + + // Direction specifies the direction of traffic the tc program should + // attach to for a given network device. + // +kubebuilder:validation:Enum=ingress;egress + Direction string `json:"direction"` // Priority specifies the priority of the tc program in relation to // other programs of the same type with the same attach point. It is a value @@ -131,11 +133,6 @@ type TcAttachInfoState struct { // +kubebuilder:validation:Maximum=1000 Priority int32 `json:"priority"` - // Direction specifies the direction of traffic the tc program should - // attach to for a given network device. - // +kubebuilder:validation:Enum=ingress;egress - Direction string `json:"direction"` - // ProceedOn allows the user to call other tc programs in chain on this exit code. // Multiple values are supported by repeating the parameter. // +kubebuilder:validation:MaxItems=11 diff --git a/apis/v1alpha1/tcxNsProgram_types.go b/apis/v1alpha1/tcxNsProgram_types.go index f2d89ea2c..082c0379c 100644 --- a/apis/v1alpha1/tcxNsProgram_types.go +++ b/apis/v1alpha1/tcxNsProgram_types.go @@ -23,9 +23,9 @@ import ( ) // +genclient -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Namespaced +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced // TcxNsProgram is the Schema for the TcxNsProgram API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -50,7 +50,7 @@ type TcxNsProgramSpec struct { BpfAppCommon `json:",inline"` } -// TcxNsProgramInfo defines the TCX Ns Program details +// TcxNsProgramInfo defines the tcx program details type TcxNsProgramInfo struct { BpfProgramCommon `json:",inline"` // The list of points to which the program should be attached. The list is @@ -64,8 +64,7 @@ type TcxNsAttachInfo struct { InterfaceSelector InterfaceSelector `json:"interfaceselector"` // Containers identifies the set of containers in which to attach the eBPF - // program. If Containers is not specified, the BPF program will be attached - // in the root network namespace. + // program. Containers ContainerNsSelector `json:"containers"` // Direction specifies the direction of traffic the tcx program should @@ -73,7 +72,7 @@ type TcxNsAttachInfo struct { // +kubebuilder:validation:Enum=ingress;egress Direction string `json:"direction"` - // Priority specifies the priority of the tc program in relation to + // Priority specifies the priority of the tcx program in relation to // other programs of the same type with the same attach point. It is a value // from 0 to 1000 where lower values have higher precedence. // +kubebuilder:validation:Minimum=0 @@ -96,17 +95,17 @@ type TcxNsProgramInfoState struct { // TcxAttachInfoState for each unique attach point. The list is optional and // may be udated after the bpf program has been loaded. // +optional - AttachPoints []TcxAttachInfoState `json:"attach_points"` + AttachPoints []TcxNsAttachInfoState `json:"attach_points"` } type TcxNsAttachInfoState struct { - AttachInfoCommon `json:",inline"` + AttachInfoStateCommon `json:",inline"` // Interface name to attach the tcx program to. IfName string `json:"ifname"` - // Optional container pid to attach the tcx program in. - ContainerPid uint32 `json:"containerpid"` + // Container pid to attach the tcx program in. + ContainerPid int32 `json:"containerpid"` // Direction specifies the direction of traffic the tcx program should // attach to for a given network device. diff --git a/apis/v1alpha1/tcxProgram_types.go b/apis/v1alpha1/tcxProgram_types.go index 31d632686..a444f1a9d 100644 --- a/apis/v1alpha1/tcxProgram_types.go +++ b/apis/v1alpha1/tcxProgram_types.go @@ -24,9 +24,9 @@ import ( // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // TcxProgram is the Schema for the TcxProgram API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -102,14 +102,14 @@ type TcxProgramInfoState struct { } type TcxAttachInfoState struct { - AttachInfoCommon `json:",inline"` + AttachInfoStateCommon `json:",inline"` // Interface name to attach the tcx program to. IfName string `json:"ifname"` // Optional container pid to attach the tcx program in. // +optional - ContainerPid *uint32 `json:"containerpid"` + ContainerPid *int32 `json:"containerpid"` // Direction specifies the direction of traffic the tcx program should // attach to for a given network device. diff --git a/apis/v1alpha1/tracepointProgram_types.go b/apis/v1alpha1/tracepointProgram_types.go index 48ddc1e47..734e8dbbc 100644 --- a/apis/v1alpha1/tracepointProgram_types.go +++ b/apis/v1alpha1/tracepointProgram_types.go @@ -24,9 +24,9 @@ import ( // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // TracepointProgram is the Schema for the TracepointPrograms API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -70,3 +70,18 @@ type TracepointProgramList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []TracepointProgram `json:"items"` } + +type TracepointProgramInfoState struct { + // The list of points to which the program should be attached. The list is + // optional and may be udated after the bpf program has been loaded. + // +optional + AttachPoints []TracepointAttachInfoState `json:"attach_points"` +} + +type TracepointAttachInfoState struct { + AttachInfoStateCommon `json:",inline"` + + // Name refers to the name of a kernel tracepoint to attach the + // bpf program to. + Name string `json:"name"` +} diff --git a/apis/v1alpha1/uprobeNsProgram_types.go b/apis/v1alpha1/uprobeNsProgram_types.go index ae3f8db28..341d224fa 100644 --- a/apis/v1alpha1/uprobeNsProgram_types.go +++ b/apis/v1alpha1/uprobeNsProgram_types.go @@ -23,9 +23,9 @@ import ( ) // +genclient -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Namespaced +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced // UprobeNsProgram is the Schema for the UprobeNsPrograms API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -78,10 +78,11 @@ type UprobeNsAttachInfo struct { // +kubebuilder:default:=false RetProbe bool `json:"retprobe"` + // ANF-TODO: Why is this Pid not used? // Only execute uprobe for given process identification number (PID). If PID // is not provided, uprobe executes for all PIDs. // +optional - Pid int32 `json:"pid"` + Pid *int32 `json:"pid"` // Containers identifies the set of containers in which to attach the uprobe. // If Containers is not specified, the uprobe will be attached in the @@ -99,3 +100,44 @@ type UprobeNsProgramList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []UprobeNsProgram `json:"items"` } + +type UprobeNsProgramInfoState struct { + // The list of points to which the program should be attached. + // UprobeNsAttachInfoState is similar to UprobeNsAttachInfo, but the + // container selector is expanded, and we have one instance of + // UprobeNsAttachInfoState for each unique attach point. The list is optional + // and may be udated after the bpf program has been loaded. + // +optional + AttachPoints []UprobeNsAttachInfoState `json:"attach_points"` +} + +type UprobeNsAttachInfoState struct { + AttachInfoStateCommon `json:",inline"` + + // Function to attach the uprobe to. + // +optional + FunctionName string `json:"func_name"` + + // Offset added to the address of the function for uprobe. + // +optional + // +kubebuilder:default:=0 + Offset uint64 `json:"offset"` + + // Library name or the absolute path to a binary or library. + Target string `json:"target"` + + // Whether the program is a uretprobe. Default is false + // +optional + // +kubebuilder:default:=false + RetProbe bool `json:"retprobe"` + + // ANF-TODO: Why is this Pid not used? + // Only execute uprobe for given process identification number (PID). If PID + // is not provided, uprobe executes for all PIDs. + // +optional + Pid *int32 `json:"pid"` + + // Container pid to attach the uprobe program in. + // +optional + ContainerPid int32 `json:"containerpid"` +} diff --git a/apis/v1alpha1/uprobeProgram_types.go b/apis/v1alpha1/uprobeProgram_types.go index 1d4d49144..59ffcc478 100644 --- a/apis/v1alpha1/uprobeProgram_types.go +++ b/apis/v1alpha1/uprobeProgram_types.go @@ -24,9 +24,9 @@ import ( // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // UprobeProgram is the Schema for the UprobePrograms API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -86,7 +86,7 @@ type UprobeAttachInfo struct { // Only execute uprobe for given process identification number (PID). If PID // is not provided, uprobe executes for all PIDs. // +optional - Pid int32 `json:"pid"` + Pid *int32 `json:"pid"` // Containers identifies the set of containers in which to attach the uprobe. // If Containers is not specified, the uprobe will be attached in the @@ -105,3 +105,43 @@ type UprobeProgramList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []UprobeProgram `json:"items"` } + +type UprobeProgramInfoState struct { + // The list of points to which the program should be attached. + // UprobeAttachInfoState is similar to UprobeAttachInfo, but the + // container selector is expanded, and we have one instance of + // UprobeAttachInfoState for each unique attach point. The list is optional + // and may be udated after the bpf program has been loaded. + // +optional + AttachPoints []UprobeAttachInfoState `json:"attach_points"` +} + +type UprobeAttachInfoState struct { + AttachInfoStateCommon `json:",inline"` + + // Function to attach the uprobe to. + // +optional + FunctionName string `json:"func_name"` + + // Offset added to the address of the function for uprobe. + // +optional + // +kubebuilder:default:=0 + Offset uint64 `json:"offset"` + + // Library name or the absolute path to a binary or library. + Target string `json:"target"` + + // Whether the program is a uretprobe. Default is false + // +optional + // +kubebuilder:default:=false + RetProbe bool `json:"retprobe"` + + // Only execute uprobe for given process identification number (PID). If PID + // is not provided, uprobe executes for all PIDs. + // +optional + Pid *int32 `json:"pid"` + + // Optional container pid to attach the uprobe program in. + // +optional + ContainerPid *int32 `json:"containerpid"` +} diff --git a/apis/v1alpha1/xdpNsProgram_types.go b/apis/v1alpha1/xdpNsProgram_types.go index 228c45273..a51787e9b 100644 --- a/apis/v1alpha1/xdpNsProgram_types.go +++ b/apis/v1alpha1/xdpNsProgram_types.go @@ -23,9 +23,9 @@ import ( ) // +genclient -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Namespaced +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced // XdpNsProgram is the Schema for the XdpNsPrograms API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -40,7 +40,7 @@ type XdpNsProgram struct { Spec XdpNsProgramSpec `json:"spec"` // +optional - Status XdpProgramStatus `json:"status,omitempty"` + Status BpfAppStatus `json:"status,omitempty"` } // XdpNsProgramSpec defines the desired state of XdpNsProgram @@ -49,7 +49,7 @@ type XdpNsProgramSpec struct { BpfAppCommon `json:",inline"` } -// XdpNsProgramInfo defines the common fields for all XdpProgram types +// XdpNsProgramInfo defines the xdp program details type XdpNsProgramInfo struct { BpfProgramCommon `json:",inline"` // The list of points to which the program should be attached. The list is @@ -82,7 +82,7 @@ type XdpNsAttachInfo struct { } // +kubebuilder:object:root=true -// XdpProgramList contains a list of XdpPrograms +// XdpNsProgramList contains a list of XdpNsPrograms type XdpNsProgramList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` @@ -91,22 +91,22 @@ type XdpNsProgramList struct { type XdpNsProgramInfoState struct { // The list of points to which the program should be attached. - // XdpAttachInfoState is similar to XdpAttachInfo, but the interface and + // XdpNsAttachInfoState is similar to XdpNsAttachInfo, but the interface and // container selectors are expanded, and we have one instance of - // XdpAttachInfoState for each unique attach point. The list is optional and + // XdpNsAttachInfoState for each unique attach point. The list is optional and // may be udated after the bpf program has been loaded. // +optional AttachPoints []XdpAttachInfoState `json:"attach_points"` } type XdpNsAttachInfoState struct { - AttachInfoCommon `json:",inline"` + AttachInfoStateCommon `json:",inline"` // Interface name to attach the xdp program to. IfName string `json:"ifname"` // Optional container pid to attach the xdp program in. - ContainerPid uint32 `json:"containerpid"` + ContainerPid int32 `json:"containerpid"` // Priority specifies the priority of the xdp program in relation to // other programs of the same type with the same attach point. It is a value diff --git a/apis/v1alpha1/xdpProgram_types.go b/apis/v1alpha1/xdpProgram_types.go index b7e6e0cef..7b1973c03 100644 --- a/apis/v1alpha1/xdpProgram_types.go +++ b/apis/v1alpha1/xdpProgram_types.go @@ -24,9 +24,9 @@ import ( // +genclient // +genclient:nonNamespaced -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster // XdpProgram is the Schema for the XdpPrograms API // +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` @@ -88,11 +88,6 @@ type XdpAttachInfo struct { ProceedOn []XdpProceedOnValue `json:"proceedon"` } -// XdpProgramStatus defines the observed state of XdpProgram -type XdpProgramStatus struct { - BpfAppStatus `json:",inline"` -} - // +kubebuilder:object:root=true // XdpProgramList contains a list of XdpPrograms type XdpProgramList struct { @@ -112,14 +107,14 @@ type XdpProgramInfoState struct { } type XdpAttachInfoState struct { - AttachInfoCommon `json:",inline"` + AttachInfoStateCommon `json:",inline"` // Interface name to attach the xdp program to. IfName string `json:"ifname"` // Optional container pid to attach the xdp program in. // +optional - ContainerPid *uint32 `json:"containerpid"` + ContainerPid *int32 `json:"containerpid"` // Priority specifies the priority of the xdp program in relation to // other programs of the same type with the same attach point. It is a value diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index d348022a7..9e0a318fd 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -26,7 +26,7 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AttachInfoCommon) DeepCopyInto(out *AttachInfoCommon) { +func (in *AttachInfoStateCommon) DeepCopyInto(out *AttachInfoStateCommon) { *out = *in if in.AttachId != nil { in, out := &in.AttachId, &out.AttachId @@ -35,12 +35,12 @@ func (in *AttachInfoCommon) DeepCopyInto(out *AttachInfoCommon) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AttachInfoCommon. -func (in *AttachInfoCommon) DeepCopy() *AttachInfoCommon { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AttachInfoStateCommon. +func (in *AttachInfoStateCommon) DeepCopy() *AttachInfoStateCommon { if in == nil { return nil } - out := new(AttachInfoCommon) + out := new(AttachInfoStateCommon) in.DeepCopyInto(out) return out } @@ -198,21 +198,11 @@ func (in *BpfApplicationProgram) DeepCopyInto(out *BpfApplicationProgram) { *out = new(KprobeProgramInfo) (*in).DeepCopyInto(*out) } - if in.Kretprobe != nil { - in, out := &in.Kretprobe, &out.Kretprobe - *out = new(KprobeProgramInfo) - (*in).DeepCopyInto(*out) - } if in.Uprobe != nil { in, out := &in.Uprobe, &out.Uprobe *out = new(UprobeProgramInfo) (*in).DeepCopyInto(*out) } - if in.Uretprobe != nil { - in, out := &in.Uretprobe, &out.Uretprobe - *out = new(UprobeProgramInfo) - (*in).DeepCopyInto(*out) - } if in.Tracepoint != nil { in, out := &in.Tracepoint, &out.Tracepoint *out = new(TracepointProgramInfo) @@ -254,6 +244,26 @@ func (in *BpfApplicationProgramState) DeepCopyInto(out *BpfApplicationProgramSta *out = new(FentryProgramInfoState) (*in).DeepCopyInto(*out) } + if in.Fexit != nil { + in, out := &in.Fexit, &out.Fexit + *out = new(FexitProgramInfoState) + (*in).DeepCopyInto(*out) + } + if in.Kprobe != nil { + in, out := &in.Kprobe, &out.Kprobe + *out = new(KprobeProgramInfoState) + (*in).DeepCopyInto(*out) + } + if in.Uprobe != nil { + in, out := &in.Uprobe, &out.Uprobe + *out = new(UprobeProgramInfoState) + (*in).DeepCopyInto(*out) + } + if in.Tracepoint != nil { + in, out := &in.Tracepoint, &out.Tracepoint + *out = new(TracepointProgramInfoState) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BpfApplicationProgramState. @@ -453,11 +463,6 @@ func (in *BpfNsApplicationProgram) DeepCopyInto(out *BpfNsApplicationProgram) { *out = new(UprobeNsProgramInfo) (*in).DeepCopyInto(*out) } - if in.Uretprobe != nil { - in, out := &in.Uretprobe, &out.Uretprobe - *out = new(UprobeNsProgramInfo) - (*in).DeepCopyInto(*out) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BpfNsApplicationProgram. @@ -479,11 +484,21 @@ func (in *BpfNsApplicationProgramState) DeepCopyInto(out *BpfNsApplicationProgra *out = new(XdpNsProgramInfoState) (*in).DeepCopyInto(*out) } + if in.TC != nil { + in, out := &in.TC, &out.TC + *out = new(TcNsProgramInfoState) + (*in).DeepCopyInto(*out) + } if in.TCX != nil { in, out := &in.TCX, &out.TCX *out = new(TcxNsProgramInfoState) (*in).DeepCopyInto(*out) } + if in.Uprobe != nil { + in, out := &in.Uprobe, &out.Uprobe + *out = new(UprobeNsProgramInfoState) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BpfNsApplicationProgramState. @@ -905,7 +920,7 @@ func (in *FentryAttachInfo) DeepCopy() *FentryAttachInfo { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FentryAttachInfoState) DeepCopyInto(out *FentryAttachInfoState) { *out = *in - in.AttachInfoCommon.DeepCopyInto(&out.AttachInfoCommon) + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FentryAttachInfoState. @@ -1059,6 +1074,22 @@ func (in *FexitAttachInfo) DeepCopy() *FexitAttachInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FexitAttachInfoState) DeepCopyInto(out *FexitAttachInfoState) { + *out = *in + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FexitAttachInfoState. +func (in *FexitAttachInfoState) DeepCopy() *FexitAttachInfoState { + if in == nil { + return nil + } + out := new(FexitAttachInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FexitLoadInfo) DeepCopyInto(out *FexitLoadInfo) { *out = *in @@ -1119,6 +1150,23 @@ func (in *FexitProgramInfo) DeepCopy() *FexitProgramInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FexitProgramInfoState) DeepCopyInto(out *FexitProgramInfoState) { + *out = *in + out.FexitLoadInfo = in.FexitLoadInfo + in.FexitAttachInfoState.DeepCopyInto(&out.FexitAttachInfoState) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FexitProgramInfoState. +func (in *FexitProgramInfoState) DeepCopy() *FexitProgramInfoState { + if in == nil { + return nil + } + out := new(FexitProgramInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FexitProgramList) DeepCopyInto(out *FexitProgramList) { *out = *in @@ -1227,6 +1275,22 @@ func (in *KprobeAttachInfo) DeepCopy() *KprobeAttachInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KprobeAttachInfoState) DeepCopyInto(out *KprobeAttachInfoState) { + *out = *in + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KprobeAttachInfoState. +func (in *KprobeAttachInfoState) DeepCopy() *KprobeAttachInfoState { + if in == nil { + return nil + } + out := new(KprobeAttachInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KprobeProgram) DeepCopyInto(out *KprobeProgram) { *out = *in @@ -1275,6 +1339,28 @@ func (in *KprobeProgramInfo) DeepCopy() *KprobeProgramInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KprobeProgramInfoState) DeepCopyInto(out *KprobeProgramInfoState) { + *out = *in + if in.AttachPoints != nil { + in, out := &in.AttachPoints, &out.AttachPoints + *out = make([]KprobeAttachInfoState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KprobeProgramInfoState. +func (in *KprobeProgramInfoState) DeepCopy() *KprobeProgramInfoState { + if in == nil { + return nil + } + out := new(KprobeProgramInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KprobeProgramList) DeepCopyInto(out *KprobeProgramList) { *out = *in @@ -1353,15 +1439,10 @@ func (in *TcAttachInfo) DeepCopy() *TcAttachInfo { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TcAttachInfoState) DeepCopyInto(out *TcAttachInfoState) { *out = *in - in.AttachInfoCommon.DeepCopyInto(&out.AttachInfoCommon) - if in.AttachId != nil { - in, out := &in.AttachId, &out.AttachId - *out = new(uint32) - **out = **in - } + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) if in.ContainerPid != nil { in, out := &in.ContainerPid, &out.ContainerPid - *out = new(uint32) + *out = new(int32) **out = **in } if in.ProceedOn != nil { @@ -1403,6 +1484,27 @@ func (in *TcNsAttachInfo) DeepCopy() *TcNsAttachInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TcNsAttachInfoState) DeepCopyInto(out *TcNsAttachInfoState) { + *out = *in + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) + if in.ProceedOn != nil { + in, out := &in.ProceedOn, &out.ProceedOn + *out = make([]TcProceedOnValue, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TcNsAttachInfoState. +func (in *TcNsAttachInfoState) DeepCopy() *TcNsAttachInfoState { + if in == nil { + return nil + } + out := new(TcNsAttachInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TcNsProgram) DeepCopyInto(out *TcNsProgram) { *out = *in @@ -1453,6 +1555,28 @@ func (in *TcNsProgramInfo) DeepCopy() *TcNsProgramInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TcNsProgramInfoState) DeepCopyInto(out *TcNsProgramInfoState) { + *out = *in + if in.AttachPoints != nil { + in, out := &in.AttachPoints, &out.AttachPoints + *out = make([]TcNsAttachInfoState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TcNsProgramInfoState. +func (in *TcNsProgramInfoState) DeepCopy() *TcNsProgramInfoState { + if in == nil { + return nil + } + out := new(TcNsProgramInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TcNsProgramList) DeepCopyInto(out *TcNsProgramList) { *out = *in @@ -1647,10 +1771,10 @@ func (in *TcxAttachInfo) DeepCopy() *TcxAttachInfo { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TcxAttachInfoState) DeepCopyInto(out *TcxAttachInfoState) { *out = *in - in.AttachInfoCommon.DeepCopyInto(&out.AttachInfoCommon) + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) if in.ContainerPid != nil { in, out := &in.ContainerPid, &out.ContainerPid - *out = new(uint32) + *out = new(int32) **out = **in } } @@ -1685,7 +1809,7 @@ func (in *TcxNsAttachInfo) DeepCopy() *TcxNsAttachInfo { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TcxNsAttachInfoState) DeepCopyInto(out *TcxNsAttachInfoState) { *out = *in - in.AttachInfoCommon.DeepCopyInto(&out.AttachInfoCommon) + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TcxNsAttachInfoState. @@ -1753,7 +1877,7 @@ func (in *TcxNsProgramInfoState) DeepCopyInto(out *TcxNsProgramInfoState) { *out = *in if in.AttachPoints != nil { in, out := &in.AttachPoints, &out.AttachPoints - *out = make([]TcxAttachInfoState, len(*in)) + *out = make([]TcxNsAttachInfoState, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1955,6 +2079,22 @@ func (in *TracepointAttachInfo) DeepCopy() *TracepointAttachInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TracepointAttachInfoState) DeepCopyInto(out *TracepointAttachInfoState) { + *out = *in + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TracepointAttachInfoState. +func (in *TracepointAttachInfoState) DeepCopy() *TracepointAttachInfoState { + if in == nil { + return nil + } + out := new(TracepointAttachInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TracepointProgram) DeepCopyInto(out *TracepointProgram) { *out = *in @@ -2003,6 +2143,28 @@ func (in *TracepointProgramInfo) DeepCopy() *TracepointProgramInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TracepointProgramInfoState) DeepCopyInto(out *TracepointProgramInfoState) { + *out = *in + if in.AttachPoints != nil { + in, out := &in.AttachPoints, &out.AttachPoints + *out = make([]TracepointAttachInfoState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TracepointProgramInfoState. +func (in *TracepointProgramInfoState) DeepCopy() *TracepointProgramInfoState { + if in == nil { + return nil + } + out := new(TracepointProgramInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TracepointProgramList) DeepCopyInto(out *TracepointProgramList) { *out = *in @@ -2055,6 +2217,11 @@ func (in *TracepointProgramSpec) DeepCopy() *TracepointProgramSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UprobeAttachInfo) DeepCopyInto(out *UprobeAttachInfo) { *out = *in + if in.Pid != nil { + in, out := &in.Pid, &out.Pid + *out = new(int32) + **out = **in + } if in.Containers != nil { in, out := &in.Containers, &out.Containers *out = new(ContainerSelector) @@ -2072,9 +2239,40 @@ func (in *UprobeAttachInfo) DeepCopy() *UprobeAttachInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UprobeAttachInfoState) DeepCopyInto(out *UprobeAttachInfoState) { + *out = *in + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) + if in.Pid != nil { + in, out := &in.Pid, &out.Pid + *out = new(int32) + **out = **in + } + if in.ContainerPid != nil { + in, out := &in.ContainerPid, &out.ContainerPid + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UprobeAttachInfoState. +func (in *UprobeAttachInfoState) DeepCopy() *UprobeAttachInfoState { + if in == nil { + return nil + } + out := new(UprobeAttachInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UprobeNsAttachInfo) DeepCopyInto(out *UprobeNsAttachInfo) { *out = *in + if in.Pid != nil { + in, out := &in.Pid, &out.Pid + *out = new(int32) + **out = **in + } in.Containers.DeepCopyInto(&out.Containers) } @@ -2088,6 +2286,27 @@ func (in *UprobeNsAttachInfo) DeepCopy() *UprobeNsAttachInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UprobeNsAttachInfoState) DeepCopyInto(out *UprobeNsAttachInfoState) { + *out = *in + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) + if in.Pid != nil { + in, out := &in.Pid, &out.Pid + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UprobeNsAttachInfoState. +func (in *UprobeNsAttachInfoState) DeepCopy() *UprobeNsAttachInfoState { + if in == nil { + return nil + } + out := new(UprobeNsAttachInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UprobeNsProgram) DeepCopyInto(out *UprobeNsProgram) { *out = *in @@ -2138,6 +2357,28 @@ func (in *UprobeNsProgramInfo) DeepCopy() *UprobeNsProgramInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UprobeNsProgramInfoState) DeepCopyInto(out *UprobeNsProgramInfoState) { + *out = *in + if in.AttachPoints != nil { + in, out := &in.AttachPoints, &out.AttachPoints + *out = make([]UprobeNsAttachInfoState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UprobeNsProgramInfoState. +func (in *UprobeNsProgramInfoState) DeepCopy() *UprobeNsProgramInfoState { + if in == nil { + return nil + } + out := new(UprobeNsProgramInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UprobeNsProgramList) DeepCopyInto(out *UprobeNsProgramList) { *out = *in @@ -2237,6 +2478,28 @@ func (in *UprobeProgramInfo) DeepCopy() *UprobeProgramInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UprobeProgramInfoState) DeepCopyInto(out *UprobeProgramInfoState) { + *out = *in + if in.AttachPoints != nil { + in, out := &in.AttachPoints, &out.AttachPoints + *out = make([]UprobeAttachInfoState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UprobeProgramInfoState. +func (in *UprobeProgramInfoState) DeepCopy() *UprobeProgramInfoState { + if in == nil { + return nil + } + out := new(UprobeProgramInfoState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UprobeProgramList) DeepCopyInto(out *UprobeProgramList) { *out = *in @@ -2315,10 +2578,10 @@ func (in *XdpAttachInfo) DeepCopy() *XdpAttachInfo { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *XdpAttachInfoState) DeepCopyInto(out *XdpAttachInfoState) { *out = *in - in.AttachInfoCommon.DeepCopyInto(&out.AttachInfoCommon) + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) if in.ContainerPid != nil { in, out := &in.ContainerPid, &out.ContainerPid - *out = new(uint32) + *out = new(int32) **out = **in } if in.ProceedOn != nil { @@ -2363,7 +2626,7 @@ func (in *XdpNsAttachInfo) DeepCopy() *XdpNsAttachInfo { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *XdpNsAttachInfoState) DeepCopyInto(out *XdpNsAttachInfoState) { *out = *in - in.AttachInfoCommon.DeepCopyInto(&out.AttachInfoCommon) + in.AttachInfoStateCommon.DeepCopyInto(&out.AttachInfoStateCommon) if in.ProceedOn != nil { in, out := &in.ProceedOn, &out.ProceedOn *out = make([]XdpProceedOnValue, len(*in)) @@ -2622,19 +2885,3 @@ func (in *XdpProgramSpec) DeepCopy() *XdpProgramSpec { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *XdpProgramStatus) DeepCopyInto(out *XdpProgramStatus) { - *out = *in - in.BpfAppStatus.DeepCopyInto(&out.BpfAppStatus) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XdpProgramStatus. -func (in *XdpProgramStatus) DeepCopy() *XdpProgramStatus { - if in == nil { - return nil - } - out := new(XdpProgramStatus) - in.DeepCopyInto(out) - return out -} diff --git a/bundle/manifests/bpfman-operator.clusterserviceversion.yaml b/bundle/manifests/bpfman-operator.clusterserviceversion.yaml index 37d27e66e..fa87232a6 100644 --- a/bundle/manifests/bpfman-operator.clusterserviceversion.yaml +++ b/bundle/manifests/bpfman-operator.clusterserviceversion.yaml @@ -609,7 +609,7 @@ metadata: capabilities: Basic Install categories: OpenShift Optional containerImage: quay.io/bpfman/bpfman-operator:latest - createdAt: "2025-02-04T19:14:55Z" + createdAt: "2025-02-05T23:18:23Z" features.operators.openshift.io/cnf: "false" features.operators.openshift.io/cni: "false" features.operators.openshift.io/csi: "true" diff --git a/bundle/manifests/bpfman.io_bpfapplications.yaml b/bundle/manifests/bpfman.io_bpfapplications.yaml index 1507d5f66..457f64d05 100644 --- a/bundle/manifests/bpfman.io_bpfapplications.yaml +++ b/bundle/manifests/bpfman.io_bpfapplications.yaml @@ -378,101 +378,7 @@ spec: items: properties: func_name: - description: Functions to attach the kprobe to. - type: string - offset: - default: 0 - description: |- - Offset added to the address of the function for kprobe. - Not allowed for kretprobes. - format: int64 - type: integer - retprobe: - default: false - description: Whether the program is a kretprobe. Default - is false - type: boolean - required: - - func_name - type: object - x-kubernetes-validations: - - message: offset cannot be set for kretprobes - rule: self.retprobe == false || self.offset == 0 - type: array - bpffunctionname: - description: |- - BpfFunctionName is the name of the function that is the entry point for the BPF - program - type: string - oldmapownerselector: - description: |- - ANF-TODO: MapOwnerSelector has been moved to BpfAppCommon. Do not use in - new load/attach split code. - - - OldMapOwnerSelector is used to select the loaded eBPF program this eBPF - program will share a map with. The value is a label applied to the - BpfProgram to select. The selector must resolve to exactly one instance - of a BpfProgram on a given node or the eBPF program will not load. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - bpffunctionname - type: object - kretprobe: - description: kretprobe defines the desired state of the application's - KretprobePrograms. - properties: - attach_points: - description: |- - The list of points to which the program should be attached. The list is - optional and may be udated after the bpf program has been loaded - items: - properties: - func_name: - description: Functions to attach the kprobe to. + description: Function to attach the kprobe to. type: string offset: default: 0 @@ -1086,9 +992,7 @@ spec: - Fentry - Fexit - Kprobe - - Kretprobe - Uprobe - - Uretprobe - Tracepoint type: string uprobe: @@ -1212,178 +1116,6 @@ spec: new load/attach split code. - OldMapOwnerSelector is used to select the loaded eBPF program this eBPF - program will share a map with. The value is a label applied to the - BpfProgram to select. The selector must resolve to exactly one instance - of a BpfProgram on a given node or the eBPF program will not load. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - bpffunctionname - type: object - uretprobe: - description: uretprobe defines the desired state of the application's - UretprobePrograms. - properties: - attach_points: - description: |- - The list of points to which the program should be attached. The list is - optional and may be udated after the bpf program has been loaded - items: - properties: - containers: - description: |- - Containers identifies the set of containers in which to attach the uprobe. - If Containers is not specified, the uprobe will be attached in the - bpfman-agent container. The ContainerSelector is very flexible and even - allows the selection of all containers in a cluster. If an attempt is - made to attach uprobes to too many containers, it can have a negative - impact on on the cluster. - properties: - containernames: - description: |- - Name(s) of container(s). If none are specified, all containers in the - pod are selected. - items: - type: string - type: array - namespace: - default: "" - description: Target namespaces. - type: string - pods: - description: |- - Target pods. This field must be specified, to select all pods use - standard metav1.LabelSelector semantics and make it empty. - properties: - matchExpressions: - description: matchExpressions is a list of - label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - pods - type: object - func_name: - description: Function to attach the uprobe to. - type: string - offset: - default: 0 - description: Offset added to the address of the function - for uprobe. - format: int64 - type: integer - pid: - description: |- - Only execute uprobe for given process identification number (PID). If PID - is not provided, uprobe executes for all PIDs. - format: int32 - type: integer - retprobe: - default: false - description: Whether the program is a uretprobe. Default - is false - type: boolean - target: - description: Library name or the absolute path to - a binary or library. - type: string - required: - - target - type: object - type: array - bpffunctionname: - description: |- - BpfFunctionName is the name of the function that is the entry point for the BPF - program - type: string - oldmapownerselector: - description: |- - ANF-TODO: MapOwnerSelector has been moved to BpfAppCommon. Do not use in - new load/attach split code. - - OldMapOwnerSelector is used to select the loaded eBPF program this eBPF program will share a map with. The value is a label applied to the BpfProgram to select. The selector must resolve to exactly one instance @@ -1650,22 +1382,10 @@ spec: forbidden otherwise rule: 'has(self.type) && self.type == ''Fexit'' ? has(self.fexit) : !has(self.fexit)' - - message: kprobe configuration is required when type is Kprobe, - and forbidden otherwise - rule: 'has(self.type) && self.type == ''Kprobe'' ? has(self.kprobe) - : !has(self.kprobe)' - - message: kretprobe configuration is required when type is Kretprobe, - and forbidden otherwise - rule: 'has(self.type) && self.type == ''Kretprobe'' ? has(self.kretprobe) - : !has(self.kretprobe)' - message: uprobe configuration is required when type is Uprobe, and forbidden otherwise rule: 'has(self.type) && self.type == ''Uprobe'' ? has(self.uprobe) : !has(self.uprobe)' - - message: uretprobe configuration is required when type is Uretprobe, - and forbidden otherwise - rule: 'has(self.type) && self.type == ''Uretprobe'' ? has(self.uretprobe) - : !has(self.uretprobe)' - message: tracepoint configuration is required when type is Tracepoint, and forbidden otherwise rule: 'has(self.type) && self.type == ''Tracepoint'' ? has(self.tracepoint) diff --git a/bundle/manifests/bpfman.io_bpfapplicationstates.yaml b/bundle/manifests/bpfman.io_bpfapplicationstates.yaml index e4150098b..1d9333f8e 100644 --- a/bundle/manifests/bpfman.io_bpfapplicationstates.yaml +++ b/bundle/manifests/bpfman.io_bpfapplicationstates.yaml @@ -27,9 +27,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: |- - BpfApplicationState contains the per-node state of a BpfApplication. - ANF-TODO: I can't get the Node to display in the kubectl output. + description: BpfApplicationState contains the per-node state of a BpfApplication. properties: apiVersion: description: |- @@ -65,15 +63,8 @@ spec: It is a map from the bpf program name to BpfApplicationProgramState elements. items: - description: |- - BpfApplicationProgramState defines the desired state of BpfApplication - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TC' ? has(self.tc) : !has(self.tc)",message="tc configuration is required when type is TC, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Fexit' ? has(self.fexit) : !has(self.fexit)",message="fexit configuration is required when type is Fexit, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Kprobe' ? has(self.kprobe) : !has(self.kprobe)",message="kprobe configuration is required when type is Kprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Kretprobe' ? has(self.kretprobe) : !has(self.kretprobe)",message="kretprobe configuration is required when type is Kretprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uretprobe' ? has(self.uretprobe) : !has(self.uretprobe)",message="uretprobe configuration is required when type is Uretprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Tracepoint' ? has(self.tracepoint) : !has(self.tracepoint)",message="tracepoint configuration is required when type is Tracepoint, and forbidden otherwise" + description: BpfApplicationProgramState defines the desired state + of BpfApplication properties: bpffunctionname: description: |- @@ -120,6 +111,101 @@ spec: - should_attach - uuid type: object + fexit: + description: fexit defines the desired state of the application's + FexitPrograms. + properties: + attach: + type: boolean + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + function_name: + description: FunctionName is the name of the function to + attach the Fexit program to. + type: string + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attach + - attachid + - attachstatus + - function_name + - should_attach + - uuid + type: object + kprobe: + description: kprobe defines the desired state of the application's + KprobePrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. The list is + optional and may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + func_name: + description: Function to attach the kprobe to. + type: string + offset: + description: |- + Offset added to the address of the function for kprobe. + Not allowed for kretprobes. + format: int64 + type: integer + retprobe: + description: Whether the program is a kretprobe. + type: boolean + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - func_name + - offset + - retprobe + - should_attach + - uuid + type: object + type: array + type: object oldmapownerselector: description: |- ANF-TODO: MapOwnerSelector has been moved to BpfAppCommon. Do not use in @@ -350,6 +436,52 @@ spec: type: object type: array type: object + tracepoint: + description: tracepoint defines the desired state of the application's + TracepointPrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. The list is + optional and may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + name: + description: |- + Name refers to the name of a kernel tracepoint to attach the + bpf program to. + type: string + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - name + - should_attach + - uuid + type: object + type: array + type: object type: description: Type specifies the bpf program type enum: @@ -359,11 +491,82 @@ spec: - Fentry - Fexit - Kprobe - - Kretprobe - Uprobe - - Uretprobe - Tracepoint type: string + uprobe: + description: uprobe defines the desired state of the application's + UprobePrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. + UprobeAttachInfoState is similar to UprobeAttachInfo, but the + container selector is expanded, and we have one instance of + UprobeAttachInfoState for each unique attach point. The list is optional + and may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + containerpid: + description: Optional container pid to attach the + uprobe program in. + format: int32 + type: integer + func_name: + description: Function to attach the uprobe to. + type: string + offset: + default: 0 + description: Offset added to the address of the function + for uprobe. + format: int64 + type: integer + pid: + description: |- + Only execute uprobe for given process identification number (PID). If PID + is not provided, uprobe executes for all PIDs. + format: int32 + type: integer + retprobe: + default: false + description: Whether the program is a uretprobe. Default + is false + type: boolean + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + target: + description: Library name or the absolute path to + a binary or library. + type: string + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - should_attach + - target + - uuid + type: object + type: array + type: object xdp: description: xdp defines the desired state of the application's XdpPrograms. @@ -452,6 +655,10 @@ spec: otherwise rule: 'has(self.type) && self.type == ''XDP'' ? has(self.xdp) : !has(self.xdp)' + - message: tc configuration is required when type is TC, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''TC'' ? has(self.tc) : + !has(self.tc)' - message: tcx configuration is required when type is TCX, and forbidden otherwise rule: 'has(self.type) && self.type == ''TCX'' ? has(self.tcx) @@ -460,6 +667,22 @@ spec: and forbidden otherwise rule: 'has(self.type) && self.type == ''Fentry'' ? has(self.fentry) : !has(self.fentry)' + - message: fexit configuration is required when type is Fexit, and + forbidden otherwise + rule: 'has(self.type) && self.type == ''Fexit'' ? has(self.fexit) + : !has(self.fexit)' + - message: kprobe configuration is required when type is Kprobe, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Kprobe'' ? has(self.kprobe) + : !has(self.kprobe)' + - message: uprobe configuration is required when type is Uprobe, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Uprobe'' ? has(self.uprobe) + : !has(self.uprobe)' + - message: tracepoint configuration is required when type is Tracepoint, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Tracepoint'' ? has(self.tracepoint) + : !has(self.tracepoint)' type: array updatecount: description: |- diff --git a/bundle/manifests/bpfman.io_bpfnsapplications.yaml b/bundle/manifests/bpfman.io_bpfnsapplications.yaml index b8c420ea3..7649ed47e 100644 --- a/bundle/manifests/bpfman.io_bpfnsapplications.yaml +++ b/bundle/manifests/bpfman.io_bpfnsapplications.yaml @@ -485,8 +485,7 @@ spec: containers: description: |- Containers identifies the set of containers in which to attach the eBPF - program. If Containers is not specified, the BPF program will be attached - in the root network namespace. + program. properties: containernames: description: |- @@ -575,7 +574,7 @@ spec: type: object priority: description: |- - Priority specifies the priority of the tc program in relation to + Priority specifies the priority of the tcx program in relation to other programs of the same type with the same attach point. It is a value from 0 to 1000 where lower values have higher precedence. format: int32 @@ -658,7 +657,6 @@ spec: - TC - TCX - Uprobe - - Uretprobe type: string uprobe: description: uprobe defines the desired state of the application's @@ -749,175 +747,7 @@ spec: type: integer pid: description: |- - Only execute uprobe for given process identification number (PID). If PID - is not provided, uprobe executes for all PIDs. - format: int32 - type: integer - retprobe: - default: false - description: Whether the program is a uretprobe. Default - is false - type: boolean - target: - description: Library name or the absolute path to - a binary or library. - type: string - required: - - containers - - target - type: object - type: array - bpffunctionname: - description: |- - BpfFunctionName is the name of the function that is the entry point for the BPF - program - type: string - oldmapownerselector: - description: |- - ANF-TODO: MapOwnerSelector has been moved to BpfAppCommon. Do not use in - new load/attach split code. - - - OldMapOwnerSelector is used to select the loaded eBPF program this eBPF - program will share a map with. The value is a label applied to the - BpfProgram to select. The selector must resolve to exactly one instance - of a BpfProgram on a given node or the eBPF program will not load. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - bpffunctionname - type: object - uretprobe: - description: uretprobe defines the desired state of the application's - UretprobeNsPrograms. - properties: - attach_points: - description: |- - The list of points to which the program should be attached. The list is - optional and may be udated after the bpf program has been loaded - items: - properties: - containers: - description: |- - Containers identifies the set of containers in which to attach the uprobe. - If Containers is not specified, the uprobe will be attached in the - bpfman-agent container. The ContainerNsSelector is very flexible and even - allows the selection of all containers in a cluster. If an attempt is - made to attach uprobes to too many containers, it can have a negative - impact on on the cluster. - properties: - containernames: - description: |- - Name(s) of container(s). If none are specified, all containers in the - pod are selected. - items: - type: string - type: array - pods: - description: |- - Target pods. This field must be specified, to select all pods use - standard metav1.LabelSelector semantics and make it empty. - properties: - matchExpressions: - description: matchExpressions is a list of - label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - pods - type: object - func_name: - description: Function to attach the uprobe to. - type: string - offset: - default: 0 - description: Offset added to the address of the function - for uprobe. - format: int64 - type: integer - pid: - description: |- + ANF-TODO: Why is this Pid not used? Only execute uprobe for given process identification number (PID). If PID is not provided, uprobe executes for all PIDs. format: int32 @@ -1205,10 +1035,6 @@ spec: and forbidden otherwise rule: 'has(self.type) && self.type == ''Uprobe'' ? has(self.uprobe) : !has(self.uprobe)' - - message: uretprobe configuration is required when type is Uretprobe, - and forbidden otherwise - rule: 'has(self.type) && self.type == ''Uretprobe'' ? has(self.uretprobe) - : !has(self.uretprobe)' minItems: 1 type: array required: diff --git a/bundle/manifests/bpfman.io_bpfnsapplicationstates.yaml b/bundle/manifests/bpfman.io_bpfnsapplicationstates.yaml index d691aa69c..42637370c 100644 --- a/bundle/manifests/bpfman.io_bpfnsapplicationstates.yaml +++ b/bundle/manifests/bpfman.io_bpfnsapplicationstates.yaml @@ -63,11 +63,8 @@ spec: It is a map from the bpf program name to BpfNsApplicationProgramState elements. items: - description: |- - BpfNsApplicationProgramState defines the desired state of BpfNsApplication - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TC' ? has(self.tc) : !has(self.tc)",message="tc configuration is required when type is TC, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uretprobe' ? has(self.uretprobe) : !has(self.uretprobe)",message="uretprobe configuration is required when type is Uretprobe, and forbidden otherwise" + description: BpfNsApplicationProgramState defines the desired state + of BpfNsApplication properties: bpffunctionname: description: |- @@ -139,6 +136,100 @@ spec: ProgramAttachStatus records whether the program should be loaded and whether the program is loaded. type: string + tc: + description: tc defines the desired state of the application's + TcPrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. + TcNsAttachInfoState is similar to TcAttachInfo, but the interface and + container selectors are expanded, and we have one instance of + TcNsAttachInfoState for each unique attach point. The list is optional and + may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + containerpid: + description: Container pid to attach the tc program + in. + format: int32 + type: integer + direction: + description: |- + Direction specifies the direction of traffic the tc program should + attach to for a given network device. + enum: + - ingress + - egress + type: string + ifname: + description: Interface name to attach the tc program + to. + type: string + priority: + description: |- + Priority specifies the priority of the tc program in relation to + other programs of the same type with the same attach point. It is a value + from 0 to 1000 where lower values have higher precedence. + format: int32 + maximum: 1000 + minimum: 0 + type: integer + proceedon: + description: |- + ProceedOn allows the user to call other tc programs in chain on this exit code. + Multiple values are supported by repeating the parameter. + items: + enum: + - unspec + - ok + - reclassify + - shot + - pipe + - stolen + - queued + - repeat + - redirect + - trap + - dispatcher_return + type: string + maxItems: 11 + type: array + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - containerpid + - direction + - ifname + - priority + - proceedon + - should_attach + - uuid + type: object + type: array + type: object tcx: description: tcx defines the desired state of the application's TcxPrograms. @@ -166,8 +257,8 @@ spec: successfully, and if not, why. type: string containerpid: - description: Optional container pid to attach the - tcx program in. + description: Container pid to attach the tcx program + in. format: int32 type: integer direction: @@ -203,6 +294,7 @@ spec: required: - attachid - attachstatus + - containerpid - direction - ifname - priority @@ -217,14 +309,82 @@ spec: - XDP - TC - TCX - - Fentry - - Fexit - - Kprobe - - Kretprobe - Uprobe - - Uretprobe - - Tracepoint type: string + uprobe: + description: uprobe defines the desired state of the application's + UprobePrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. + UprobeNsAttachInfoState is similar to UprobeNsAttachInfo, but the + container selector is expanded, and we have one instance of + UprobeNsAttachInfoState for each unique attach point. The list is optional + and may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + containerpid: + description: Container pid to attach the uprobe program + in. + format: int32 + type: integer + func_name: + description: Function to attach the uprobe to. + type: string + offset: + default: 0 + description: Offset added to the address of the function + for uprobe. + format: int64 + type: integer + pid: + description: |- + ANF-TODO: Why is this Pid not used? + Only execute uprobe for given process identification number (PID). If PID + is not provided, uprobe executes for all PIDs. + format: int32 + type: integer + retprobe: + default: false + description: Whether the program is a uretprobe. Default + is false + type: boolean + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + target: + description: Library name or the absolute path to + a binary or library. + type: string + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - should_attach + - target + - uuid + type: object + type: array + type: object xdp: description: xdp defines the desired state of the application's XdpPrograms. @@ -232,9 +392,9 @@ spec: attach_points: description: |- The list of points to which the program should be attached. - XdpAttachInfoState is similar to XdpAttachInfo, but the interface and + XdpNsAttachInfoState is similar to XdpNsAttachInfo, but the interface and container selectors are expanded, and we have one instance of - XdpAttachInfoState for each unique attach point. The list is optional and + XdpNsAttachInfoState for each unique attach point. The list is optional and may be udated after the bpf program has been loaded. items: properties: @@ -313,10 +473,18 @@ spec: otherwise rule: 'has(self.type) && self.type == ''XDP'' ? has(self.xdp) : !has(self.xdp)' + - message: tc configuration is required when type is TC, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''TC'' ? has(self.tc) : + !has(self.tc)' - message: tcx configuration is required when type is TCX, and forbidden otherwise rule: 'has(self.type) && self.type == ''TCX'' ? has(self.tcx) : !has(self.tcx)' + - message: uprobe configuration is required when type is Uprobe, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Uprobe'' ? has(self.uprobe) + : !has(self.uprobe)' type: array updatecount: description: |- diff --git a/bundle/manifests/bpfman.io_kprobeprograms.yaml b/bundle/manifests/bpfman.io_kprobeprograms.yaml index d8cbe3a32..41a911c94 100644 --- a/bundle/manifests/bpfman.io_kprobeprograms.yaml +++ b/bundle/manifests/bpfman.io_kprobeprograms.yaml @@ -68,7 +68,7 @@ spec: items: properties: func_name: - description: Functions to attach the kprobe to. + description: Function to attach the kprobe to. type: string offset: default: 0 diff --git a/bundle/manifests/bpfman.io_tcxnsprograms.yaml b/bundle/manifests/bpfman.io_tcxnsprograms.yaml index 1fffe83ed..e6772ba72 100644 --- a/bundle/manifests/bpfman.io_tcxnsprograms.yaml +++ b/bundle/manifests/bpfman.io_tcxnsprograms.yaml @@ -74,8 +74,7 @@ spec: containers: description: |- Containers identifies the set of containers in which to attach the eBPF - program. If Containers is not specified, the BPF program will be attached - in the root network namespace. + program. properties: containernames: description: |- @@ -163,7 +162,7 @@ spec: type: object priority: description: |- - Priority specifies the priority of the tc program in relation to + Priority specifies the priority of the tcx program in relation to other programs of the same type with the same attach point. It is a value from 0 to 1000 where lower values have higher precedence. format: int32 diff --git a/bundle/manifests/bpfman.io_uprobensprograms.yaml b/bundle/manifests/bpfman.io_uprobensprograms.yaml index 43bad0bcf..ad146ffc2 100644 --- a/bundle/manifests/bpfman.io_uprobensprograms.yaml +++ b/bundle/manifests/bpfman.io_uprobensprograms.yaml @@ -153,6 +153,7 @@ spec: type: integer pid: description: |- + ANF-TODO: Why is this Pid not used? Only execute uprobe for given process identification number (PID). If PID is not provided, uprobe executes for all PIDs. format: int32 diff --git a/bundle/manifests/bpfman.io_xdpnsprograms.yaml b/bundle/manifests/bpfman.io_xdpnsprograms.yaml index 88c25c37d..003ddf6b1 100644 --- a/bundle/manifests/bpfman.io_xdpnsprograms.yaml +++ b/bundle/manifests/bpfman.io_xdpnsprograms.yaml @@ -400,7 +400,8 @@ spec: - nodeselector type: object status: - description: XdpProgramStatus defines the observed state of XdpProgram + description: BpfAppStatus reflects the status of a BpfApplication or BpfApplicationState + object properties: conditions: description: |- diff --git a/config/crd/bases/bpfman.io_bpfapplications.yaml b/config/crd/bases/bpfman.io_bpfapplications.yaml index e7b87d71e..c84034a0a 100644 --- a/config/crd/bases/bpfman.io_bpfapplications.yaml +++ b/config/crd/bases/bpfman.io_bpfapplications.yaml @@ -378,101 +378,7 @@ spec: items: properties: func_name: - description: Functions to attach the kprobe to. - type: string - offset: - default: 0 - description: |- - Offset added to the address of the function for kprobe. - Not allowed for kretprobes. - format: int64 - type: integer - retprobe: - default: false - description: Whether the program is a kretprobe. Default - is false - type: boolean - required: - - func_name - type: object - x-kubernetes-validations: - - message: offset cannot be set for kretprobes - rule: self.retprobe == false || self.offset == 0 - type: array - bpffunctionname: - description: |- - BpfFunctionName is the name of the function that is the entry point for the BPF - program - type: string - oldmapownerselector: - description: |- - ANF-TODO: MapOwnerSelector has been moved to BpfAppCommon. Do not use in - new load/attach split code. - - - OldMapOwnerSelector is used to select the loaded eBPF program this eBPF - program will share a map with. The value is a label applied to the - BpfProgram to select. The selector must resolve to exactly one instance - of a BpfProgram on a given node or the eBPF program will not load. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - bpffunctionname - type: object - kretprobe: - description: kretprobe defines the desired state of the application's - KretprobePrograms. - properties: - attach_points: - description: |- - The list of points to which the program should be attached. The list is - optional and may be udated after the bpf program has been loaded - items: - properties: - func_name: - description: Functions to attach the kprobe to. + description: Function to attach the kprobe to. type: string offset: default: 0 @@ -1086,9 +992,7 @@ spec: - Fentry - Fexit - Kprobe - - Kretprobe - Uprobe - - Uretprobe - Tracepoint type: string uprobe: @@ -1212,178 +1116,6 @@ spec: new load/attach split code. - OldMapOwnerSelector is used to select the loaded eBPF program this eBPF - program will share a map with. The value is a label applied to the - BpfProgram to select. The selector must resolve to exactly one instance - of a BpfProgram on a given node or the eBPF program will not load. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - bpffunctionname - type: object - uretprobe: - description: uretprobe defines the desired state of the application's - UretprobePrograms. - properties: - attach_points: - description: |- - The list of points to which the program should be attached. The list is - optional and may be udated after the bpf program has been loaded - items: - properties: - containers: - description: |- - Containers identifies the set of containers in which to attach the uprobe. - If Containers is not specified, the uprobe will be attached in the - bpfman-agent container. The ContainerSelector is very flexible and even - allows the selection of all containers in a cluster. If an attempt is - made to attach uprobes to too many containers, it can have a negative - impact on on the cluster. - properties: - containernames: - description: |- - Name(s) of container(s). If none are specified, all containers in the - pod are selected. - items: - type: string - type: array - namespace: - default: "" - description: Target namespaces. - type: string - pods: - description: |- - Target pods. This field must be specified, to select all pods use - standard metav1.LabelSelector semantics and make it empty. - properties: - matchExpressions: - description: matchExpressions is a list of - label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - pods - type: object - func_name: - description: Function to attach the uprobe to. - type: string - offset: - default: 0 - description: Offset added to the address of the function - for uprobe. - format: int64 - type: integer - pid: - description: |- - Only execute uprobe for given process identification number (PID). If PID - is not provided, uprobe executes for all PIDs. - format: int32 - type: integer - retprobe: - default: false - description: Whether the program is a uretprobe. Default - is false - type: boolean - target: - description: Library name or the absolute path to - a binary or library. - type: string - required: - - target - type: object - type: array - bpffunctionname: - description: |- - BpfFunctionName is the name of the function that is the entry point for the BPF - program - type: string - oldmapownerselector: - description: |- - ANF-TODO: MapOwnerSelector has been moved to BpfAppCommon. Do not use in - new load/attach split code. - - OldMapOwnerSelector is used to select the loaded eBPF program this eBPF program will share a map with. The value is a label applied to the BpfProgram to select. The selector must resolve to exactly one instance @@ -1650,22 +1382,10 @@ spec: forbidden otherwise rule: 'has(self.type) && self.type == ''Fexit'' ? has(self.fexit) : !has(self.fexit)' - - message: kprobe configuration is required when type is Kprobe, - and forbidden otherwise - rule: 'has(self.type) && self.type == ''Kprobe'' ? has(self.kprobe) - : !has(self.kprobe)' - - message: kretprobe configuration is required when type is Kretprobe, - and forbidden otherwise - rule: 'has(self.type) && self.type == ''Kretprobe'' ? has(self.kretprobe) - : !has(self.kretprobe)' - message: uprobe configuration is required when type is Uprobe, and forbidden otherwise rule: 'has(self.type) && self.type == ''Uprobe'' ? has(self.uprobe) : !has(self.uprobe)' - - message: uretprobe configuration is required when type is Uretprobe, - and forbidden otherwise - rule: 'has(self.type) && self.type == ''Uretprobe'' ? has(self.uretprobe) - : !has(self.uretprobe)' - message: tracepoint configuration is required when type is Tracepoint, and forbidden otherwise rule: 'has(self.type) && self.type == ''Tracepoint'' ? has(self.tracepoint) diff --git a/config/crd/bases/bpfman.io_bpfapplicationstates.yaml b/config/crd/bases/bpfman.io_bpfapplicationstates.yaml index 9693b4fdf..1735ef48b 100644 --- a/config/crd/bases/bpfman.io_bpfapplicationstates.yaml +++ b/config/crd/bases/bpfman.io_bpfapplicationstates.yaml @@ -27,9 +27,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: |- - BpfApplicationState contains the per-node state of a BpfApplication. - ANF-TODO: I can't get the Node to display in the kubectl output. + description: BpfApplicationState contains the per-node state of a BpfApplication. properties: apiVersion: description: |- @@ -65,15 +63,8 @@ spec: It is a map from the bpf program name to BpfApplicationProgramState elements. items: - description: |- - BpfApplicationProgramState defines the desired state of BpfApplication - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TC' ? has(self.tc) : !has(self.tc)",message="tc configuration is required when type is TC, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Fexit' ? has(self.fexit) : !has(self.fexit)",message="fexit configuration is required when type is Fexit, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Kprobe' ? has(self.kprobe) : !has(self.kprobe)",message="kprobe configuration is required when type is Kprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Kretprobe' ? has(self.kretprobe) : !has(self.kretprobe)",message="kretprobe configuration is required when type is Kretprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uretprobe' ? has(self.uretprobe) : !has(self.uretprobe)",message="uretprobe configuration is required when type is Uretprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Tracepoint' ? has(self.tracepoint) : !has(self.tracepoint)",message="tracepoint configuration is required when type is Tracepoint, and forbidden otherwise" + description: BpfApplicationProgramState defines the desired state + of BpfApplication properties: bpffunctionname: description: |- @@ -120,6 +111,101 @@ spec: - should_attach - uuid type: object + fexit: + description: fexit defines the desired state of the application's + FexitPrograms. + properties: + attach: + type: boolean + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + function_name: + description: FunctionName is the name of the function to + attach the Fexit program to. + type: string + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attach + - attachid + - attachstatus + - function_name + - should_attach + - uuid + type: object + kprobe: + description: kprobe defines the desired state of the application's + KprobePrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. The list is + optional and may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + func_name: + description: Function to attach the kprobe to. + type: string + offset: + description: |- + Offset added to the address of the function for kprobe. + Not allowed for kretprobes. + format: int64 + type: integer + retprobe: + description: Whether the program is a kretprobe. + type: boolean + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - func_name + - offset + - retprobe + - should_attach + - uuid + type: object + type: array + type: object oldmapownerselector: description: |- ANF-TODO: MapOwnerSelector has been moved to BpfAppCommon. Do not use in @@ -350,6 +436,52 @@ spec: type: object type: array type: object + tracepoint: + description: tracepoint defines the desired state of the application's + TracepointPrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. The list is + optional and may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + name: + description: |- + Name refers to the name of a kernel tracepoint to attach the + bpf program to. + type: string + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - name + - should_attach + - uuid + type: object + type: array + type: object type: description: Type specifies the bpf program type enum: @@ -359,11 +491,82 @@ spec: - Fentry - Fexit - Kprobe - - Kretprobe - Uprobe - - Uretprobe - Tracepoint type: string + uprobe: + description: uprobe defines the desired state of the application's + UprobePrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. + UprobeAttachInfoState is similar to UprobeAttachInfo, but the + container selector is expanded, and we have one instance of + UprobeAttachInfoState for each unique attach point. The list is optional + and may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + containerpid: + description: Optional container pid to attach the + uprobe program in. + format: int32 + type: integer + func_name: + description: Function to attach the uprobe to. + type: string + offset: + default: 0 + description: Offset added to the address of the function + for uprobe. + format: int64 + type: integer + pid: + description: |- + Only execute uprobe for given process identification number (PID). If PID + is not provided, uprobe executes for all PIDs. + format: int32 + type: integer + retprobe: + default: false + description: Whether the program is a uretprobe. Default + is false + type: boolean + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + target: + description: Library name or the absolute path to + a binary or library. + type: string + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - should_attach + - target + - uuid + type: object + type: array + type: object xdp: description: xdp defines the desired state of the application's XdpPrograms. @@ -452,6 +655,10 @@ spec: otherwise rule: 'has(self.type) && self.type == ''XDP'' ? has(self.xdp) : !has(self.xdp)' + - message: tc configuration is required when type is TC, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''TC'' ? has(self.tc) : + !has(self.tc)' - message: tcx configuration is required when type is TCX, and forbidden otherwise rule: 'has(self.type) && self.type == ''TCX'' ? has(self.tcx) @@ -460,6 +667,22 @@ spec: and forbidden otherwise rule: 'has(self.type) && self.type == ''Fentry'' ? has(self.fentry) : !has(self.fentry)' + - message: fexit configuration is required when type is Fexit, and + forbidden otherwise + rule: 'has(self.type) && self.type == ''Fexit'' ? has(self.fexit) + : !has(self.fexit)' + - message: kprobe configuration is required when type is Kprobe, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Kprobe'' ? has(self.kprobe) + : !has(self.kprobe)' + - message: uprobe configuration is required when type is Uprobe, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Uprobe'' ? has(self.uprobe) + : !has(self.uprobe)' + - message: tracepoint configuration is required when type is Tracepoint, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Tracepoint'' ? has(self.tracepoint) + : !has(self.tracepoint)' type: array updatecount: description: |- diff --git a/config/crd/bases/bpfman.io_bpfnsapplications.yaml b/config/crd/bases/bpfman.io_bpfnsapplications.yaml index 7c9744283..3fd0d8ce4 100644 --- a/config/crd/bases/bpfman.io_bpfnsapplications.yaml +++ b/config/crd/bases/bpfman.io_bpfnsapplications.yaml @@ -485,8 +485,7 @@ spec: containers: description: |- Containers identifies the set of containers in which to attach the eBPF - program. If Containers is not specified, the BPF program will be attached - in the root network namespace. + program. properties: containernames: description: |- @@ -575,7 +574,7 @@ spec: type: object priority: description: |- - Priority specifies the priority of the tc program in relation to + Priority specifies the priority of the tcx program in relation to other programs of the same type with the same attach point. It is a value from 0 to 1000 where lower values have higher precedence. format: int32 @@ -658,7 +657,6 @@ spec: - TC - TCX - Uprobe - - Uretprobe type: string uprobe: description: uprobe defines the desired state of the application's @@ -749,175 +747,7 @@ spec: type: integer pid: description: |- - Only execute uprobe for given process identification number (PID). If PID - is not provided, uprobe executes for all PIDs. - format: int32 - type: integer - retprobe: - default: false - description: Whether the program is a uretprobe. Default - is false - type: boolean - target: - description: Library name or the absolute path to - a binary or library. - type: string - required: - - containers - - target - type: object - type: array - bpffunctionname: - description: |- - BpfFunctionName is the name of the function that is the entry point for the BPF - program - type: string - oldmapownerselector: - description: |- - ANF-TODO: MapOwnerSelector has been moved to BpfAppCommon. Do not use in - new load/attach split code. - - - OldMapOwnerSelector is used to select the loaded eBPF program this eBPF - program will share a map with. The value is a label applied to the - BpfProgram to select. The selector must resolve to exactly one instance - of a BpfProgram on a given node or the eBPF program will not load. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - bpffunctionname - type: object - uretprobe: - description: uretprobe defines the desired state of the application's - UretprobeNsPrograms. - properties: - attach_points: - description: |- - The list of points to which the program should be attached. The list is - optional and may be udated after the bpf program has been loaded - items: - properties: - containers: - description: |- - Containers identifies the set of containers in which to attach the uprobe. - If Containers is not specified, the uprobe will be attached in the - bpfman-agent container. The ContainerNsSelector is very flexible and even - allows the selection of all containers in a cluster. If an attempt is - made to attach uprobes to too many containers, it can have a negative - impact on on the cluster. - properties: - containernames: - description: |- - Name(s) of container(s). If none are specified, all containers in the - pod are selected. - items: - type: string - type: array - pods: - description: |- - Target pods. This field must be specified, to select all pods use - standard metav1.LabelSelector semantics and make it empty. - properties: - matchExpressions: - description: matchExpressions is a list of - label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - required: - - pods - type: object - func_name: - description: Function to attach the uprobe to. - type: string - offset: - default: 0 - description: Offset added to the address of the function - for uprobe. - format: int64 - type: integer - pid: - description: |- + ANF-TODO: Why is this Pid not used? Only execute uprobe for given process identification number (PID). If PID is not provided, uprobe executes for all PIDs. format: int32 @@ -1205,10 +1035,6 @@ spec: and forbidden otherwise rule: 'has(self.type) && self.type == ''Uprobe'' ? has(self.uprobe) : !has(self.uprobe)' - - message: uretprobe configuration is required when type is Uretprobe, - and forbidden otherwise - rule: 'has(self.type) && self.type == ''Uretprobe'' ? has(self.uretprobe) - : !has(self.uretprobe)' minItems: 1 type: array required: diff --git a/config/crd/bases/bpfman.io_bpfnsapplicationstates.yaml b/config/crd/bases/bpfman.io_bpfnsapplicationstates.yaml index 8ab457833..afbd7c911 100644 --- a/config/crd/bases/bpfman.io_bpfnsapplicationstates.yaml +++ b/config/crd/bases/bpfman.io_bpfnsapplicationstates.yaml @@ -63,11 +63,8 @@ spec: It is a map from the bpf program name to BpfNsApplicationProgramState elements. items: - description: |- - BpfNsApplicationProgramState defines the desired state of BpfNsApplication - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'TC' ? has(self.tc) : !has(self.tc)",message="tc configuration is required when type is TC, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uprobe' ? has(self.uprobe) : !has(self.uprobe)",message="uprobe configuration is required when type is Uprobe, and forbidden otherwise" - // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Uretprobe' ? has(self.uretprobe) : !has(self.uretprobe)",message="uretprobe configuration is required when type is Uretprobe, and forbidden otherwise" + description: BpfNsApplicationProgramState defines the desired state + of BpfNsApplication properties: bpffunctionname: description: |- @@ -139,6 +136,100 @@ spec: ProgramAttachStatus records whether the program should be loaded and whether the program is loaded. type: string + tc: + description: tc defines the desired state of the application's + TcPrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. + TcNsAttachInfoState is similar to TcAttachInfo, but the interface and + container selectors are expanded, and we have one instance of + TcNsAttachInfoState for each unique attach point. The list is optional and + may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + containerpid: + description: Container pid to attach the tc program + in. + format: int32 + type: integer + direction: + description: |- + Direction specifies the direction of traffic the tc program should + attach to for a given network device. + enum: + - ingress + - egress + type: string + ifname: + description: Interface name to attach the tc program + to. + type: string + priority: + description: |- + Priority specifies the priority of the tc program in relation to + other programs of the same type with the same attach point. It is a value + from 0 to 1000 where lower values have higher precedence. + format: int32 + maximum: 1000 + minimum: 0 + type: integer + proceedon: + description: |- + ProceedOn allows the user to call other tc programs in chain on this exit code. + Multiple values are supported by repeating the parameter. + items: + enum: + - unspec + - ok + - reclassify + - shot + - pipe + - stolen + - queued + - repeat + - redirect + - trap + - dispatcher_return + type: string + maxItems: 11 + type: array + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - containerpid + - direction + - ifname + - priority + - proceedon + - should_attach + - uuid + type: object + type: array + type: object tcx: description: tcx defines the desired state of the application's TcxPrograms. @@ -166,8 +257,8 @@ spec: successfully, and if not, why. type: string containerpid: - description: Optional container pid to attach the - tcx program in. + description: Container pid to attach the tcx program + in. format: int32 type: integer direction: @@ -203,6 +294,7 @@ spec: required: - attachid - attachstatus + - containerpid - direction - ifname - priority @@ -217,14 +309,82 @@ spec: - XDP - TC - TCX - - Fentry - - Fexit - - Kprobe - - Kretprobe - Uprobe - - Uretprobe - - Tracepoint type: string + uprobe: + description: uprobe defines the desired state of the application's + UprobePrograms. + properties: + attach_points: + description: |- + The list of points to which the program should be attached. + UprobeNsAttachInfoState is similar to UprobeNsAttachInfo, but the + container selector is expanded, and we have one instance of + UprobeNsAttachInfoState for each unique attach point. The list is optional + and may be udated after the bpf program has been loaded. + items: + properties: + attachid: + description: |- + An identifier for the attach point assigned by bpfman. This field is + empty until the program is successfully attached and bpfman returns the + id. + ANF-TODO: For the POC, this will be the program ID. + format: int32 + type: integer + attachstatus: + description: |- + AttachStatus reflects whether the attachment has been reconciled + successfully, and if not, why. + type: string + containerpid: + description: Container pid to attach the uprobe program + in. + format: int32 + type: integer + func_name: + description: Function to attach the uprobe to. + type: string + offset: + default: 0 + description: Offset added to the address of the function + for uprobe. + format: int64 + type: integer + pid: + description: |- + ANF-TODO: Why is this Pid not used? + Only execute uprobe for given process identification number (PID). If PID + is not provided, uprobe executes for all PIDs. + format: int32 + type: integer + retprobe: + default: false + description: Whether the program is a uretprobe. Default + is false + type: boolean + should_attach: + description: ShouldAttach reflects whether the attachment + should exist. + type: boolean + target: + description: Library name or the absolute path to + a binary or library. + type: string + uuid: + description: |- + ANF-TODO: Putting a uuid here for now to maintain compatibility with the + existing BpfProgram. + type: string + required: + - attachid + - attachstatus + - should_attach + - target + - uuid + type: object + type: array + type: object xdp: description: xdp defines the desired state of the application's XdpPrograms. @@ -232,9 +392,9 @@ spec: attach_points: description: |- The list of points to which the program should be attached. - XdpAttachInfoState is similar to XdpAttachInfo, but the interface and + XdpNsAttachInfoState is similar to XdpNsAttachInfo, but the interface and container selectors are expanded, and we have one instance of - XdpAttachInfoState for each unique attach point. The list is optional and + XdpNsAttachInfoState for each unique attach point. The list is optional and may be udated after the bpf program has been loaded. items: properties: @@ -313,10 +473,18 @@ spec: otherwise rule: 'has(self.type) && self.type == ''XDP'' ? has(self.xdp) : !has(self.xdp)' + - message: tc configuration is required when type is TC, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''TC'' ? has(self.tc) : + !has(self.tc)' - message: tcx configuration is required when type is TCX, and forbidden otherwise rule: 'has(self.type) && self.type == ''TCX'' ? has(self.tcx) : !has(self.tcx)' + - message: uprobe configuration is required when type is Uprobe, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Uprobe'' ? has(self.uprobe) + : !has(self.uprobe)' type: array updatecount: description: |- diff --git a/config/crd/bases/bpfman.io_kprobeprograms.yaml b/config/crd/bases/bpfman.io_kprobeprograms.yaml index ee4f9ee99..0cbc3479e 100644 --- a/config/crd/bases/bpfman.io_kprobeprograms.yaml +++ b/config/crd/bases/bpfman.io_kprobeprograms.yaml @@ -68,7 +68,7 @@ spec: items: properties: func_name: - description: Functions to attach the kprobe to. + description: Function to attach the kprobe to. type: string offset: default: 0 diff --git a/config/crd/bases/bpfman.io_tcxnsprograms.yaml b/config/crd/bases/bpfman.io_tcxnsprograms.yaml index 7afb69adb..b92ddd42d 100644 --- a/config/crd/bases/bpfman.io_tcxnsprograms.yaml +++ b/config/crd/bases/bpfman.io_tcxnsprograms.yaml @@ -74,8 +74,7 @@ spec: containers: description: |- Containers identifies the set of containers in which to attach the eBPF - program. If Containers is not specified, the BPF program will be attached - in the root network namespace. + program. properties: containernames: description: |- @@ -163,7 +162,7 @@ spec: type: object priority: description: |- - Priority specifies the priority of the tc program in relation to + Priority specifies the priority of the tcx program in relation to other programs of the same type with the same attach point. It is a value from 0 to 1000 where lower values have higher precedence. format: int32 diff --git a/config/crd/bases/bpfman.io_uprobensprograms.yaml b/config/crd/bases/bpfman.io_uprobensprograms.yaml index 8c4da41fd..87add83d6 100644 --- a/config/crd/bases/bpfman.io_uprobensprograms.yaml +++ b/config/crd/bases/bpfman.io_uprobensprograms.yaml @@ -153,6 +153,7 @@ spec: type: integer pid: description: |- + ANF-TODO: Why is this Pid not used? Only execute uprobe for given process identification number (PID). If PID is not provided, uprobe executes for all PIDs. format: int32 diff --git a/config/crd/bases/bpfman.io_xdpnsprograms.yaml b/config/crd/bases/bpfman.io_xdpnsprograms.yaml index 5b63d021a..f421628c9 100644 --- a/config/crd/bases/bpfman.io_xdpnsprograms.yaml +++ b/config/crd/bases/bpfman.io_xdpnsprograms.yaml @@ -400,7 +400,8 @@ spec: - nodeselector type: object status: - description: XdpProgramStatus defines the observed state of XdpProgram + description: BpfAppStatus reflects the status of a BpfApplication or BpfApplicationState + object properties: conditions: description: |- diff --git a/controllers/app-agent/cl-application-program.go b/controllers/app-agent/cl-application-program.go index 6409ed673..8afad62f7 100644 --- a/controllers/app-agent/cl-application-program.go +++ b/controllers/app-agent/cl-application-program.go @@ -192,7 +192,7 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque // can't load the code. r.Logger.Error(err, "failed to reconcileLoad") objectChanged, _ := r.updateBpfAppStateSpec(ctx, bpfAppStateOriginal, bpfAppStateNew) - statusChanged := r.updateStatus(ctx, r, bpfmaniov1alpha1.ProgramReconcileError) + statusChanged, _ := r.updateStatus(ctx, r, bpfmaniov1alpha1.ProgramReconcileError) if statusChanged || objectChanged { return ctrl.Result{Requeue: true, RequeueAfter: retryDurationAgent}, nil } else { @@ -243,11 +243,55 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque }, } - // case bpfmaniov1alpha1.ProgTypeFexit: - // case bpfmaniov1alpha1.ProgTypeKprobe: - // case bpfmaniov1alpha1.ProgTypeUprobe: - // case bpfmaniov1alpha1.ProgTypeTracepoint: - // case bpfmaniov1alpha1.ProgTypeTC: + case bpfmaniov1alpha1.ProgTypeFexit: + rec = &FexitProgramReconciler{ + ReconcilerCommon: r.ReconcilerCommon, + ProgramReconcilerCommon: ProgramReconcilerCommon{ + appCommon: r.currentApp.Spec.BpfAppCommon, + currentProgram: prog, + currentProgramState: progState, + }, + } + + case bpfmaniov1alpha1.ProgTypeKprobe: + rec = &KprobeProgramReconciler{ + ReconcilerCommon: r.ReconcilerCommon, + ProgramReconcilerCommon: ProgramReconcilerCommon{ + appCommon: r.currentApp.Spec.BpfAppCommon, + currentProgram: prog, + currentProgramState: progState, + }, + } + + case bpfmaniov1alpha1.ProgTypeUprobe: + rec = &UprobeProgramReconciler{ + ReconcilerCommon: r.ReconcilerCommon, + ProgramReconcilerCommon: ProgramReconcilerCommon{ + appCommon: r.currentApp.Spec.BpfAppCommon, + currentProgram: prog, + currentProgramState: progState, + }, + } + + case bpfmaniov1alpha1.ProgTypeTracepoint: + rec = &TracepointProgramReconciler{ + ReconcilerCommon: r.ReconcilerCommon, + ProgramReconcilerCommon: ProgramReconcilerCommon{ + appCommon: r.currentApp.Spec.BpfAppCommon, + currentProgram: prog, + currentProgramState: progState, + }, + } + + case bpfmaniov1alpha1.ProgTypeTC: + rec = &TcProgramReconciler{ + ReconcilerCommon: r.ReconcilerCommon, + ProgramReconcilerCommon: ProgramReconcilerCommon{ + appCommon: r.currentApp.Spec.BpfAppCommon, + currentProgram: prog, + currentProgramState: progState, + }, + } case bpfmaniov1alpha1.ProgTypeTCX: rec = &TcxProgramReconciler{ @@ -301,7 +345,10 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{Requeue: true, RequeueAfter: retryDurationAgent}, nil } - statusChanged := r.updateStatus(ctx, r, bpfApplicationStatus) + statusChanged, err := r.updateStatus(ctx, r, bpfApplicationStatus) + if err != nil { + return ctrl.Result{Requeue: true, RequeueAfter: retryDurationAgent}, nil + } if specChanged || statusChanged { r.Logger.Info("BpfApplicationState updated", "Name", r.currentAppState.Name, "Spec Changed", @@ -499,6 +546,7 @@ func (r *BpfApplicationReconciler) initializeNodeProgramList(bpfAppState *bpfman } for _, prog := range r.currentApp.Spec.Programs { + // ANF-TODO: Create issue to investigate doing this with CRD validation. // Check if it's already on the list. If it is, this is an error // because a given bpf function can only be loaded once per // BpfApplication. @@ -518,35 +566,56 @@ func (r *BpfApplicationReconciler) initializeNodeProgramList(bpfAppState *bpfman progState.Fentry = &bpfmaniov1alpha1.FentryProgramInfoState{ FentryLoadInfo: prog.Fentry.FentryLoadInfo, FentryAttachInfoState: bpfmaniov1alpha1.FentryAttachInfoState{ - AttachInfoCommon: bpfmaniov1alpha1.AttachInfoCommon{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, UUID: uuid.New().String(), }, Attach: prog.Fentry.Attach, }, } + case bpfmaniov1alpha1.ProgTypeFexit: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) + progState.Fexit = &bpfmaniov1alpha1.FexitProgramInfoState{ + FexitLoadInfo: prog.Fexit.FexitLoadInfo, + FexitAttachInfoState: bpfmaniov1alpha1.FexitAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ + AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, + UUID: uuid.New().String(), + }, + Attach: prog.Fexit.Attach, + }, + } + case bpfmaniov1alpha1.ProgTypeKprobe: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) - case bpfmaniov1alpha1.ProgTypeKretprobe: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) + progState.Kprobe = &bpfmaniov1alpha1.KprobeProgramInfoState{ + AttachPoints: []bpfmaniov1alpha1.KprobeAttachInfoState{}, + } + case bpfmaniov1alpha1.ProgTypeTC: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) + progState.TC = &bpfmaniov1alpha1.TcProgramInfoState{ + AttachPoints: []bpfmaniov1alpha1.TcAttachInfoState{}, + } + case bpfmaniov1alpha1.ProgTypeTCX: progState.TCX = &bpfmaniov1alpha1.TcxProgramInfoState{ AttachPoints: []bpfmaniov1alpha1.TcxAttachInfoState{}, } + case bpfmaniov1alpha1.ProgTypeTracepoint: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) + progState.Tracepoint = &bpfmaniov1alpha1.TracepointProgramInfoState{ + AttachPoints: []bpfmaniov1alpha1.TracepointAttachInfoState{}, + } + case bpfmaniov1alpha1.ProgTypeUprobe: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) - case bpfmaniov1alpha1.ProgTypeUretprobe: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) + progState.Uprobe = &bpfmaniov1alpha1.UprobeProgramInfoState{ + AttachPoints: []bpfmaniov1alpha1.UprobeAttachInfoState{}, + } + case bpfmaniov1alpha1.ProgTypeXDP: progState.XDP = &bpfmaniov1alpha1.XdpProgramInfoState{ AttachPoints: []bpfmaniov1alpha1.XdpAttachInfoState{}, } + default: panic(fmt.Sprintf("unexpected EBPFProgType: %#v", prog.Type)) } diff --git a/controllers/app-agent/cl-application-program_test.go b/controllers/app-agent/cl-application-program_test.go index b7ae4a3db..e9af4ad61 100644 --- a/controllers/app-agent/cl-application-program_test.go +++ b/controllers/app-agent/cl-application-program_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package appagent import ( @@ -24,16 +40,25 @@ import ( func TestBpfApplicationControllerCreate(t *testing.T) { var ( // global config - appProgramName = "fakeAppProgram" - namespace = "bpfman" - bytecodePath = "/tmp/hello.o" - xdpBpfFunctionName = "XdpTest" - tcxBpfFunctionName = "TcxTest" - fentryBpfFunctionName = "FentryTest" - fentryAttachFunction = "FentryAttachTest" - priority = 50 - fakeNode = testutils.NewNode("fake-control-plane") - fakeInt0 = "eth0" + appProgramName = "fakeAppProgram" + namespace = "bpfman" + bytecodePath = "/tmp/hello.o" + + fentryBpfFunctionName = "FentryTest" + fexitBpfFunctionName = "FexitTest" + uprobeBpfFunctionName = "UprobeTest" + kprobeBpfFunctionName = "KprobeTest" + tracepointBpfFunctionName = "TracepointTest" + tcBpfFunctionName = "TcTest" + tcxBpfFunctionName = "TcxTest" + xdpBpfFunctionName = "XdpTest" + + direction = "ingress" + AttachName = "AttachNameTest" + priority = 50 + fakeNode = testutils.NewNode("fake-control-plane") + fakeInt0 = "eth0" + fakePid = int32(1000) // fakeInt1 = "eth1" ctx = context.TODO() ) @@ -46,16 +71,15 @@ func TestBpfApplicationControllerCreate(t *testing.T) { Interfaces: &fakeInts, } + // Keep XDP as the first program because there's a test that assumes it's at programs[0]. proceedOn := []bpfmaniov1alpha1.XdpProceedOnValue{bpfmaniov1alpha1.XdpProceedOnValue("pass"), bpfmaniov1alpha1.XdpProceedOnValue("dispatcher_return")} - xdpAttachInfo := bpfmaniov1alpha1.XdpAttachInfo{ InterfaceSelector: interfaceSelector, Containers: nil, Priority: int32(priority), ProceedOn: proceedOn, } - xdpProgram := bpfmaniov1alpha1.BpfApplicationProgram{ BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ BpfFunctionName: xdpBpfFunctionName, @@ -65,41 +89,124 @@ func TestBpfApplicationControllerCreate(t *testing.T) { AttachPoints: []bpfmaniov1alpha1.XdpAttachInfo{xdpAttachInfo}, }, } - programs = append(programs, xdpProgram) - tcxAttachInfo := bpfmaniov1alpha1.TcxAttachInfo{ + fentryProgram := bpfmaniov1alpha1.BpfApplicationProgram{ + BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ + BpfFunctionName: fentryBpfFunctionName, + }, + Type: bpfmaniov1alpha1.ProgTypeFentry, + Fentry: &bpfmaniov1alpha1.FentryProgramInfo{ + FentryLoadInfo: bpfmaniov1alpha1.FentryLoadInfo{ + FunctionName: AttachName, + }, + FentryAttachInfo: bpfmaniov1alpha1.FentryAttachInfo{ + Attach: true, + }, + }, + } + programs = append(programs, fentryProgram) + + fexitProgram := bpfmaniov1alpha1.BpfApplicationProgram{ + BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ + BpfFunctionName: fexitBpfFunctionName, + }, + Type: bpfmaniov1alpha1.ProgTypeFexit, + Fexit: &bpfmaniov1alpha1.FexitProgramInfo{ + FexitLoadInfo: bpfmaniov1alpha1.FexitLoadInfo{ + FunctionName: AttachName, + }, + FexitAttachInfo: bpfmaniov1alpha1.FexitAttachInfo{ + Attach: true, + }, + }, + } + programs = append(programs, fexitProgram) + + kprobeProgram := bpfmaniov1alpha1.BpfApplicationProgram{ + BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ + BpfFunctionName: kprobeBpfFunctionName, + }, + Type: bpfmaniov1alpha1.ProgTypeKprobe, + Kprobe: &bpfmaniov1alpha1.KprobeProgramInfo{ + AttachPoints: []bpfmaniov1alpha1.KprobeAttachInfo{ + { + FunctionName: AttachName, + Offset: 200, + RetProbe: false, + }, + }, + }, + } + programs = append(programs, kprobeProgram) + + uprobeProgram := bpfmaniov1alpha1.BpfApplicationProgram{ + BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ + BpfFunctionName: uprobeBpfFunctionName, + }, + Type: bpfmaniov1alpha1.ProgTypeUprobe, + Uprobe: &bpfmaniov1alpha1.UprobeProgramInfo{ + AttachPoints: []bpfmaniov1alpha1.UprobeAttachInfo{ + { + FunctionName: AttachName, + Offset: 200, + Target: "/bin/bash", + RetProbe: false, + Pid: &fakePid, + }, + }, + }, + } + programs = append(programs, uprobeProgram) + + tracepointProgram := bpfmaniov1alpha1.BpfApplicationProgram{ + BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ + BpfFunctionName: tracepointBpfFunctionName, + }, + Type: bpfmaniov1alpha1.ProgTypeTracepoint, + Tracepoint: &bpfmaniov1alpha1.TracepointProgramInfo{ + AttachPoints: []bpfmaniov1alpha1.TracepointAttachInfo{ + { + Name: AttachName, + }, + }, + }, + } + programs = append(programs, tracepointProgram) + + tcAttachInfo := bpfmaniov1alpha1.TcAttachInfo{ InterfaceSelector: interfaceSelector, Containers: nil, - Direction: "ingress", + Direction: direction, Priority: int32(priority), } tcProgram := bpfmaniov1alpha1.BpfApplicationProgram{ BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ - BpfFunctionName: tcxBpfFunctionName, + BpfFunctionName: tcBpfFunctionName, }, - Type: bpfmaniov1alpha1.ProgTypeTCX, - TCX: &bpfmaniov1alpha1.TcxProgramInfo{ - AttachPoints: []bpfmaniov1alpha1.TcxAttachInfo{tcxAttachInfo}, + Type: bpfmaniov1alpha1.ProgTypeTC, + TC: &bpfmaniov1alpha1.TcProgramInfo{ + AttachPoints: []bpfmaniov1alpha1.TcAttachInfo{tcAttachInfo}, }, } programs = append(programs, tcProgram) - fentryProgram := bpfmaniov1alpha1.BpfApplicationProgram{ + tcxAttachInfo := bpfmaniov1alpha1.TcxAttachInfo{ + InterfaceSelector: interfaceSelector, + Containers: nil, + Direction: "ingress", + Priority: int32(priority), + } + tcxProgram := bpfmaniov1alpha1.BpfApplicationProgram{ BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ - BpfFunctionName: fentryBpfFunctionName, + BpfFunctionName: tcxBpfFunctionName, }, - Type: bpfmaniov1alpha1.ProgTypeFentry, - Fentry: &bpfmaniov1alpha1.FentryProgramInfo{ - FentryLoadInfo: bpfmaniov1alpha1.FentryLoadInfo{ - FunctionName: fentryAttachFunction, - }, - FentryAttachInfo: bpfmaniov1alpha1.FentryAttachInfo{ - Attach: true, - }, + Type: bpfmaniov1alpha1.ProgTypeTCX, + TCX: &bpfmaniov1alpha1.TcxProgramInfo{ + AttachPoints: []bpfmaniov1alpha1.TcxAttachInfo{tcxAttachInfo}, }, } - programs = append(programs, fentryProgram) + programs = append(programs, tcxProgram) bpfApp := &bpfmaniov1alpha1.BpfApplication{ ObjectMeta: metav1.ObjectMeta{ diff --git a/controllers/app-agent/cl-fentry-program.go b/controllers/app-agent/cl-fentry-program.go index 021490b4e..cbb0f4c98 100644 --- a/controllers/app-agent/cl-fentry-program.go +++ b/controllers/app-agent/cl-fentry-program.go @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -//lint:file-ignore U1000 Linter claims functions unused, but are required for generic - package appagent import ( @@ -26,8 +24,6 @@ import ( bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/app-agent/internal" internal "github.com/bpfman/bpfman-operator/internal" gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" - - v1 "k8s.io/api/core/v1" ) //+kubebuilder:rbac:groups=bpfman.io,resources=fentryprograms,verbs=get;list;watch @@ -47,14 +43,6 @@ func (r *FentryProgramReconciler) getProgType() internal.ProgramType { return internal.Tracing } -func (r *FentryProgramReconciler) getNode() *v1.Node { - return r.ourNode -} - -func (r *FentryProgramReconciler) getBpfGlobalData() map[string][]byte { - return r.appCommon.GlobalData -} - func (r *FentryProgramReconciler) shouldAttach() bool { return r.currentProgramState.Fentry.ShouldAttach } diff --git a/controllers/app-agent/cl-fexit-program.go b/controllers/app-agent/cl-fexit-program.go new file mode 100644 index 000000000..c228e810f --- /dev/null +++ b/controllers/app-agent/cl-fexit-program.go @@ -0,0 +1,151 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package appagent + +import ( + "context" + "fmt" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/app-agent/internal" + internal "github.com/bpfman/bpfman-operator/internal" + gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" +) + +// FexitProgramReconciler contains the info required to reconcile a +// FexitProgram +type FexitProgramReconciler struct { + ReconcilerCommon + ProgramReconcilerCommon +} + +func (r *FexitProgramReconciler) getProgId() *uint32 { + return r.currentProgramState.ProgramId +} + +func (r *FexitProgramReconciler) getProgType() internal.ProgramType { + return internal.Tracing +} + +func (r *FexitProgramReconciler) shouldAttach() bool { + return r.currentProgramState.Fexit.ShouldAttach +} + +func (r *FexitProgramReconciler) getUUID() string { + return r.currentProgramState.Fexit.UUID +} + +func (r *FexitProgramReconciler) getAttachId() *uint32 { + return r.currentProgramState.Fexit.AttachId +} + +func (r *FexitProgramReconciler) setAttachId(id *uint32) { + r.currentProgramState.Fexit.AttachId = id +} + +func (r *FexitProgramReconciler) setAttachStatus(status bpfmaniov1alpha1.BpfProgramConditionType) bool { + return updateSimpleStatus(&r.currentProgramState.Fexit.AttachStatus, status) +} + +func (r *FexitProgramReconciler) getAttachStatus() bpfmaniov1alpha1.BpfProgramConditionType { + return r.currentProgramState.Fexit.AttachStatus +} + +func (r *FexitProgramReconciler) getLoadRequest(mapOwnerId *uint32) (*gobpfman.LoadRequest, error) { + r.Logger.Info("Getting load request", "bpfFunctionName", r.currentProgram.BpfFunctionName, + "mapOwnerId", mapOwnerId, "ByteCode", r.appCommon.ByteCode) + + bytecode, err := bpfmanagentinternal.GetBytecode(r.Client, &r.appCommon.ByteCode) + if err != nil { + return nil, fmt.Errorf("failed to process bytecode selector: %v", err) + } + + // ANF-TODO: This is a temporary workaround for backwards compatibility. + // Fix it after old code removed. + var bpfFunctionName string + if r.currentProgram.BpfFunctionName != "" { + bpfFunctionName = r.currentProgram.BpfFunctionName + } else { + bpfFunctionName = r.currentProgram.Fexit.BpfFunctionName + } + + loadRequest := gobpfman.LoadRequest{ + Bytecode: bytecode, + Name: bpfFunctionName, + ProgramType: uint32(internal.Tracing), + Attach: &gobpfman.AttachInfo{ + Info: &gobpfman.AttachInfo_FexitAttachInfo{ + FexitAttachInfo: &gobpfman.FexitAttachInfo{ + FnName: r.currentProgram.Fexit.FunctionName, + }, + }, + }, + Metadata: map[string]string{internal.UuidMetadataKey: string(r.currentProgramState.Fexit.UUID), + internal.ProgramNameKey: "BpfApplication"}, + GlobalData: r.appCommon.GlobalData, + MapOwnerId: mapOwnerId, + } + + return &loadRequest, nil +} + +// updateAttachInfo processes the *ProgramInfo and updates the list of attach +// points contained in *AttachInfoState. +func (r *FexitProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDeleted bool) error { + r.Logger.Info("Fexit updateAttachInfo()", "isBeingDeleted", isBeingDeleted) + + r.currentProgramState.Fexit.Attach = r.currentProgram.Fexit.Attach + + if isBeingDeleted { + // If the program is being deleted, we don't need to do anything else. + // + // ANF-TODO: When we have load/attach split, we shouldn't even need to + // set ShouldAttach to false above, because unloading the program should + // remove all attachments and updateAttachInfo won't be called. We + // probably should delete AttachPoints when unloading the program. + + r.currentProgramState.Fexit.ShouldAttach = false + return nil + } + + r.currentProgramState.Fexit.ShouldAttach = r.currentProgram.Fexit.Attach + + return nil +} + +// processAttachInfo processes the attach points in *AttachInfoState. Based on +// the current state, it calls bpfman to attach or detach, or does nothing if +// the state is correct. It returns a boolean indicating if any changes were +// made. +// +// ANF-TODO: Generalize this function and move it into common. +func (r *FexitProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerStatus *MapOwnerParamStatus) error { + r.Logger.Info("Processing attach info", "bpfFunctionName", r.currentProgram.Fexit.BpfFunctionName, + "mapOwnerStatus", mapOwnerStatus) + + // Get existing ebpf state from bpfman. + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) + if err != nil { + r.Logger.Error(err, "failed to list loaded bpfman programs") + updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) + return fmt.Errorf("failed to list loaded bpfman programs: %v", err) + } + + _, err = r.reconcileBpfAttachment(ctx, r, loadedBpfPrograms, mapOwnerStatus) + + return err +} diff --git a/controllers/app-agent/cl-kprobe-program.go b/controllers/app-agent/cl-kprobe-program.go new file mode 100644 index 000000000..351126750 --- /dev/null +++ b/controllers/app-agent/cl-kprobe-program.go @@ -0,0 +1,253 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package appagent + +import ( + "context" + "fmt" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/app-agent/internal" + internal "github.com/bpfman/bpfman-operator/internal" + gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" + "github.com/google/uuid" +) + +// KprobeProgramReconciler contains the info required to reconcile a KprobeProgram +type KprobeProgramReconciler struct { + ReconcilerCommon + ProgramReconcilerCommon + currentAttachPoint *bpfmaniov1alpha1.KprobeAttachInfoState +} + +func (r *KprobeProgramReconciler) getProgId() *uint32 { + return r.currentProgramState.ProgramId +} + +func (r *KprobeProgramReconciler) getProgType() internal.ProgramType { + return internal.Tc +} + +func (r *KprobeProgramReconciler) shouldAttach() bool { + return r.currentAttachPoint.ShouldAttach +} + +func (r *KprobeProgramReconciler) getUUID() string { + return r.currentAttachPoint.UUID +} + +func (r *KprobeProgramReconciler) getAttachId() *uint32 { + return r.currentAttachPoint.AttachId +} + +func (r *KprobeProgramReconciler) setAttachId(id *uint32) { + r.currentAttachPoint.AttachId = id +} + +func (r *KprobeProgramReconciler) setAttachStatus(status bpfmaniov1alpha1.BpfProgramConditionType) bool { + return updateSimpleStatus(&r.currentAttachPoint.AttachStatus, status) +} + +func (r *KprobeProgramReconciler) getAttachStatus() bpfmaniov1alpha1.BpfProgramConditionType { + return r.currentAttachPoint.AttachStatus +} + +func (r *KprobeProgramReconciler) getLoadRequest(mapOwnerId *uint32) (*gobpfman.LoadRequest, error) { + + r.Logger.Info("Getting load request", "bpfFunctionName", r.currentProgram.BpfFunctionName, "reqAttachInfo", r.currentAttachPoint, "mapOwnerId", + mapOwnerId, "ByteCode", r.appCommon.ByteCode) + + bytecode, err := bpfmanagentinternal.GetBytecode(r.Client, &r.appCommon.ByteCode) + if err != nil { + return nil, fmt.Errorf("failed to process bytecode selector: %v", err) + } + + attachInfo := &gobpfman.KprobeAttachInfo{ + FnName: r.currentAttachPoint.FunctionName, + Offset: r.currentAttachPoint.Offset, + Retprobe: r.currentAttachPoint.RetProbe, + } + + // ANF-TODO: This is a temporary workaround for backwards compatibility. + // Fix it after old code removed. + var bpfFunctionName string + if r.currentProgram.BpfFunctionName != "" { + bpfFunctionName = r.currentProgram.BpfFunctionName + } else { + bpfFunctionName = r.currentProgram.Kprobe.BpfFunctionName + } + + loadRequest := gobpfman.LoadRequest{ + Bytecode: bytecode, + Name: bpfFunctionName, + ProgramType: uint32(internal.Tc), + Attach: &gobpfman.AttachInfo{ + Info: &gobpfman.AttachInfo_KprobeAttachInfo{ + KprobeAttachInfo: attachInfo, + }, + }, + Metadata: map[string]string{internal.UuidMetadataKey: string(r.currentAttachPoint.UUID), internal.ProgramNameKey: "BpfApplication"}, + GlobalData: r.appCommon.GlobalData, + MapOwnerId: mapOwnerId, + } + + return &loadRequest, nil +} + +// updateAttachInfo processes the *ProgramInfo and updates the list of attach +// points contained in *AttachInfoState. +func (r *KprobeProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDeleted bool) error { + r.Logger.Info("Kprobe updateAttachInfo()", "isBeingDeleted", isBeingDeleted) + + // Set ShouldAttach for all attach points in the node CRD to false. We'll + // update this in the next step for all attach points that are still + // present. + for i := range r.currentProgramState.Kprobe.AttachPoints { + r.currentProgramState.Kprobe.AttachPoints[i].ShouldAttach = false + } + + if isBeingDeleted { + // If the program is being deleted, we don't need to do anything else. + // + // ANF-TODO: When we have load/attach split, we shouldn't even need to + // set ShouldAttach to false above, because unloading the program should + // remove all attachments and updateAttachInfo won't be called. We + // probably should delete AttachPoints when unloading the program. + return nil + } + + for _, attachInfo := range r.currentProgram.Kprobe.AttachPoints { + expectedAttachPoints, error := r.getExpectedAttachPoints(attachInfo) + if error != nil { + return fmt.Errorf("failed to get node attach points: %v", error) + } + for _, attachPoint := range expectedAttachPoints { + index := r.findAttachPoint(attachPoint) + if index != nil { + // Attach point already exists, so set ShouldAttach to true. + r.currentProgramState.Kprobe.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true + } else { + // Attach point doesn't exist, so add it. + r.Logger.Info("Attach point doesn't exist. Adding it.") + r.currentProgramState.Kprobe.AttachPoints = append(r.currentProgramState.Kprobe.AttachPoints, attachPoint) + } + } + } + + // If any existing attach point is no longer on a list of expected attach + // points, ShouldAttach will remain set to false and it will get detached in + // a following step. + + return nil +} + +// ANF-TODO: Confirm what constitutes a match between two attach points. E.g., +// what if everything the same, but the priority and/or proceed_on values are +// different? +func (r *KprobeProgramReconciler) findAttachPoint(attachInfoState bpfmaniov1alpha1.KprobeAttachInfoState) *int { + for i, a := range r.currentProgramState.Kprobe.AttachPoints { + // attachInfoState is the same as a if the the following fields are the + // same: IfName, ContainerPid, Priority, and Direction. + if a.FunctionName == attachInfoState.FunctionName && a.Offset == attachInfoState.Offset && + a.RetProbe == attachInfoState.RetProbe { + return &i + } + } + return nil +} + +// processAttachInfo processes the attach points in *AttachInfoState. Based on +// the current state, it calls bpfman to attach or detach, or does nothing if +// the state is correct. It returns a boolean indicating if any changes were +// made. +// +// ANF-TODO: Generalize this function and move it into common. +func (r *KprobeProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerStatus *MapOwnerParamStatus) error { + r.Logger.Info("Processing attach info", "bpfFunctionName", r.currentProgram.Kprobe.BpfFunctionName, + "mapOwnerStatus", mapOwnerStatus) + + // Get existing ebpf state from bpfman. + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) + if err != nil { + r.Logger.Error(err, "failed to list loaded bpfman programs") + updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) + return fmt.Errorf("failed to list loaded bpfman programs: %v", err) + } + + // The following map is used to keep track of attach points that need to be + // removed. If it's not empty at the end of the loop, we'll remove the + // attach points. + attachPointsToRemove := make(map[int]bool) + + var lastReconcileAttachmentError error = nil + for i := range r.currentProgramState.Kprobe.AttachPoints { + r.currentAttachPoint = &r.currentProgramState.Kprobe.AttachPoints[i] + remove, err := r.reconcileBpfAttachment(ctx, r, loadedBpfPrograms, mapOwnerStatus) + if err != nil { + r.Logger.Error(err, "failed to reconcile bpf attachment", "index", i) + // All errors are logged, but the last error is saved to return and + // we continue to process the rest of the attach points so errors + // don't block valid attach points. + lastReconcileAttachmentError = err + } + + if remove { + r.Logger.Info("Marking attach point for removal", "index", i) + attachPointsToRemove[i] = true + } + } + + if len(attachPointsToRemove) > 0 { + r.Logger.Info("Removing attach points", "attachPointsToRemove", attachPointsToRemove) + r.currentProgramState.Kprobe.AttachPoints = r.removeAttachPoints(r.currentProgramState.Kprobe.AttachPoints, attachPointsToRemove) + } + + return lastReconcileAttachmentError +} + +// removeAttachPoints removes attach points from a slice of attach points based on the keys in the map. +func (r *KprobeProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.KprobeAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.KprobeAttachInfoState { + var remainingAttachPoints []bpfmaniov1alpha1.KprobeAttachInfoState + for i, a := range attachPoints { + if _, ok := attachPointsToRemove[i]; !ok { + remainingAttachPoints = append(remainingAttachPoints, a) + } + } + return remainingAttachPoints +} + +// getExpectedAttachPoints expands *AttachInfo into a list of specific attach +// points. +func (r *KprobeProgramReconciler) getExpectedAttachPoints(attachInfo bpfmaniov1alpha1.KprobeAttachInfo, +) ([]bpfmaniov1alpha1.KprobeAttachInfoState, error) { + nodeAttachPoints := []bpfmaniov1alpha1.KprobeAttachInfoState{} + + attachPoint := bpfmaniov1alpha1.KprobeAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ + ShouldAttach: true, + UUID: uuid.New().String(), + AttachId: nil, + AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, + }, + FunctionName: attachInfo.FunctionName, + Offset: attachInfo.Offset, + RetProbe: attachInfo.RetProbe, + } + nodeAttachPoints = append(nodeAttachPoints, attachPoint) + + return nodeAttachPoints, nil +} diff --git a/controllers/app-agent/cl-tc-program.go b/controllers/app-agent/cl-tc-program.go new file mode 100644 index 000000000..46af74ad3 --- /dev/null +++ b/controllers/app-agent/cl-tc-program.go @@ -0,0 +1,344 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package appagent + +import ( + "context" + "fmt" + "reflect" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/app-agent/internal" + internal "github.com/bpfman/bpfman-operator/internal" + gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" + "github.com/google/uuid" +) + +// TcProgramReconciler contains the info required to reconcile a TcProgram +type TcProgramReconciler struct { + ReconcilerCommon + ProgramReconcilerCommon + currentAttachPoint *bpfmaniov1alpha1.TcAttachInfoState +} + +func (r *TcProgramReconciler) getProgId() *uint32 { + return r.currentProgramState.ProgramId +} + +func (r *TcProgramReconciler) getProgType() internal.ProgramType { + return internal.Tc +} + +func (r *TcProgramReconciler) shouldAttach() bool { + return r.currentAttachPoint.ShouldAttach +} + +func (r *TcProgramReconciler) getUUID() string { + return r.currentAttachPoint.UUID +} + +func (r *TcProgramReconciler) getAttachId() *uint32 { + return r.currentAttachPoint.AttachId +} + +func (r *TcProgramReconciler) setAttachId(id *uint32) { + r.currentAttachPoint.AttachId = id +} + +func (r *TcProgramReconciler) setAttachStatus(status bpfmaniov1alpha1.BpfProgramConditionType) bool { + return updateSimpleStatus(&r.currentAttachPoint.AttachStatus, status) +} + +func (r *TcProgramReconciler) getAttachStatus() bpfmaniov1alpha1.BpfProgramConditionType { + return r.currentAttachPoint.AttachStatus +} + +// Must match with bpfman internal types +func tcProceedOnToInt(proceedOn []bpfmaniov1alpha1.TcProceedOnValue) []int32 { + var out []int32 + + for _, p := range proceedOn { + switch p { + case "unspec": + out = append(out, -1) + case "ok": + out = append(out, 0) + case "reclassify": + out = append(out, 1) + case "shot": + out = append(out, 2) + case "pipe": + out = append(out, 3) + case "stolen": + out = append(out, 4) + case "queued": + out = append(out, 5) + case "repeat": + out = append(out, 6) + case "redirect": + out = append(out, 7) + case "trap": + out = append(out, 8) + case "dispatcher_return": + out = append(out, 30) + } + } + + return out +} + +func (r *TcProgramReconciler) getLoadRequest(mapOwnerId *uint32) (*gobpfman.LoadRequest, error) { + + r.Logger.Info("Getting load request", "bpfFunctionName", r.currentProgram.BpfFunctionName, "reqAttachInfo", r.currentAttachPoint, "mapOwnerId", + mapOwnerId, "ByteCode", r.appCommon.ByteCode) + + bytecode, err := bpfmanagentinternal.GetBytecode(r.Client, &r.appCommon.ByteCode) + if err != nil { + return nil, fmt.Errorf("failed to process bytecode selector: %v", err) + } + + attachInfo := &gobpfman.TCAttachInfo{ + Priority: r.currentAttachPoint.Priority, + Iface: r.currentAttachPoint.IfName, + Direction: r.currentAttachPoint.Direction, + ProceedOn: tcProceedOnToInt(r.currentAttachPoint.ProceedOn), + } + + if r.currentAttachPoint.ContainerPid != nil { + netns := fmt.Sprintf("/host/proc/%d/ns/net", *r.currentAttachPoint.ContainerPid) + attachInfo.Netns = &netns + } + + // ANF-TODO: This is a temporary workaround for backwards compatibility. + // Fix it after old code removed. + var bpfFunctionName string + if r.currentProgram.BpfFunctionName != "" { + bpfFunctionName = r.currentProgram.BpfFunctionName + } else { + bpfFunctionName = r.currentProgram.TC.BpfFunctionName + } + + loadRequest := gobpfman.LoadRequest{ + Bytecode: bytecode, + Name: bpfFunctionName, + ProgramType: uint32(internal.Tc), + Attach: &gobpfman.AttachInfo{ + Info: &gobpfman.AttachInfo_TcAttachInfo{ + TcAttachInfo: attachInfo, + }, + }, + Metadata: map[string]string{internal.UuidMetadataKey: string(r.currentAttachPoint.UUID), internal.ProgramNameKey: "BpfApplication"}, + GlobalData: r.appCommon.GlobalData, + MapOwnerId: mapOwnerId, + } + + return &loadRequest, nil +} + +// updateAttachInfo processes the *ProgramInfo and updates the list of attach +// points contained in *AttachInfoState. +func (r *TcProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDeleted bool) error { + r.Logger.Info("TC updateAttachInfo()", "isBeingDeleted", isBeingDeleted) + + // Set ShouldAttach for all attach points in the node CRD to false. We'll + // update this in the next step for all attach points that are still + // present. + for i := range r.currentProgramState.TC.AttachPoints { + r.currentProgramState.TC.AttachPoints[i].ShouldAttach = false + } + + if isBeingDeleted { + // If the program is being deleted, we don't need to do anything else. + // + // ANF-TODO: When we have load/attach split, we shouldn't even need to + // set ShouldAttach to false above, because unloading the program should + // remove all attachments and updateAttachInfo won't be called. We + // probably should delete AttachPoints when unloading the program. + return nil + } + + for _, attachInfo := range r.currentProgram.TC.AttachPoints { + expectedAttachPoints, error := r.getExpectedAttachPoints(ctx, attachInfo) + if error != nil { + return fmt.Errorf("failed to get node attach points: %v", error) + } + for _, attachPoint := range expectedAttachPoints { + index := r.findAttachPoint(attachPoint) + if index != nil { + // Attach point already exists, so set ShouldAttach to true. + r.currentProgramState.TC.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true + } else { + // Attach point doesn't exist, so add it. + r.Logger.Info("Attach point doesn't exist. Adding it.") + r.currentProgramState.TC.AttachPoints = append(r.currentProgramState.TC.AttachPoints, attachPoint) + } + } + } + + // If any existing attach point is no longer on a list of expected attach + // points, ShouldAttach will remain set to false and it will get detached in + // a following step. + + return nil +} + +// ANF-TODO: Confirm what constitutes a match between two attach points. E.g., +// what if everything the same, but the priority and/or proceed_on values are +// different? +func (r *TcProgramReconciler) findAttachPoint(attachInfoState bpfmaniov1alpha1.TcAttachInfoState) *int { + for i, a := range r.currentProgramState.TC.AttachPoints { + // attachInfoState is the same as a if the the following fields are the + // same: IfName, ContainerPid, Priority, and ProceedOn. + if a.IfName == attachInfoState.IfName && a.Direction == attachInfoState.Direction && + a.Priority == attachInfoState.Priority && + reflect.DeepEqual(a.ContainerPid, attachInfoState.ContainerPid) && + reflect.DeepEqual(a.ProceedOn, attachInfoState.ProceedOn) { + return &i + } + } + return nil +} + +// processAttachInfo processes the attach points in *AttachInfoState. Based on +// the current state, it calls bpfman to attach or detach, or does nothing if +// the state is correct. It returns a boolean indicating if any changes were +// made. +// +// ANF-TODO: Generalize this function and move it into common. +func (r *TcProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerStatus *MapOwnerParamStatus) error { + r.Logger.Info("Processing attach info", "bpfFunctionName", r.currentProgram.TC.BpfFunctionName, + "mapOwnerStatus", mapOwnerStatus) + + // Get existing ebpf state from bpfman. + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) + if err != nil { + r.Logger.Error(err, "failed to list loaded bpfman programs") + updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) + return fmt.Errorf("failed to list loaded bpfman programs: %v", err) + } + + // The following map is used to keep track of attach points that need to be + // removed. If it's not empty at the end of the loop, we'll remove the + // attach points. + attachPointsToRemove := make(map[int]bool) + + var lastReconcileAttachmentError error = nil + for i := range r.currentProgramState.TC.AttachPoints { + r.currentAttachPoint = &r.currentProgramState.TC.AttachPoints[i] + remove, err := r.reconcileBpfAttachment(ctx, r, loadedBpfPrograms, mapOwnerStatus) + if err != nil { + r.Logger.Error(err, "failed to reconcile bpf attachment", "index", i) + // All errors are logged, but the last error is saved to return and + // we continue to process the rest of the attach points so errors + // don't block valid attach points. + lastReconcileAttachmentError = err + } + + if remove { + r.Logger.Info("Marking attach point for removal", "index", i) + attachPointsToRemove[i] = true + } + } + + if len(attachPointsToRemove) > 0 { + r.Logger.Info("Removing attach points", "attachPointsToRemove", attachPointsToRemove) + r.currentProgramState.TC.AttachPoints = r.removeAttachPoints(r.currentProgramState.TC.AttachPoints, attachPointsToRemove) + } + + return lastReconcileAttachmentError +} + +// removeAttachPoints removes attach points from a slice of attach points based on the keys in the map. +func (r *TcProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.TcAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.TcAttachInfoState { + var remainingAttachPoints []bpfmaniov1alpha1.TcAttachInfoState + for i, a := range attachPoints { + if _, ok := attachPointsToRemove[i]; !ok { + remainingAttachPoints = append(remainingAttachPoints, a) + } + } + return remainingAttachPoints +} + +// getExpectedAttachPoints expands *AttachInfo into a list of specific attach +// points. +func (r *TcProgramReconciler) getExpectedAttachPoints(ctx context.Context, attachInfo bpfmaniov1alpha1.TcAttachInfo, +) ([]bpfmaniov1alpha1.TcAttachInfoState, error) { + interfaces, err := getInterfaces(&attachInfo.InterfaceSelector, r.ourNode) + if err != nil { + return nil, fmt.Errorf("failed to get interfaces for TcProgram: %v", err) + } + + nodeAttachPoints := []bpfmaniov1alpha1.TcAttachInfoState{} + + if attachInfo.Containers != nil { + // There is a container selector, so see if there are any matching + // containers on this node. + containerInfo, err := r.Containers.GetContainers( + ctx, + attachInfo.Containers.Namespace, + attachInfo.Containers.Pods, + attachInfo.Containers.ContainerNames, + r.Logger, + ) + if err != nil { + return nil, fmt.Errorf("failed to get container pids: %v", err) + } + + if containerInfo != nil && len(*containerInfo) != 0 { + // Containers were found, so create attach points. + for i := range *containerInfo { + container := (*containerInfo)[i] + for _, iface := range interfaces { + containerPid := int32(container.pid) + attachPoint := bpfmaniov1alpha1.TcAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ + ShouldAttach: true, + UUID: uuid.New().String(), + AttachId: nil, + AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, + }, + IfName: iface, + ContainerPid: &containerPid, + Priority: attachInfo.Priority, + Direction: attachInfo.Direction, + ProceedOn: attachInfo.ProceedOn, + } + nodeAttachPoints = append(nodeAttachPoints, attachPoint) + } + } + } + } else { + for _, iface := range interfaces { + attachPoint := bpfmaniov1alpha1.TcAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ + ShouldAttach: true, + UUID: uuid.New().String(), + AttachId: nil, + AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, + }, + IfName: iface, + ContainerPid: nil, + Priority: attachInfo.Priority, + Direction: attachInfo.Direction, + ProceedOn: attachInfo.ProceedOn, + } + nodeAttachPoints = append(nodeAttachPoints, attachPoint) + } + } + + return nodeAttachPoints, nil +} diff --git a/controllers/app-agent/cl-tcx-program.go b/controllers/app-agent/cl-tcx-program.go index 8b67b3d44..70378a62c 100644 --- a/controllers/app-agent/cl-tcx-program.go +++ b/controllers/app-agent/cl-tcx-program.go @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -//lint:file-ignore U1000 Linter claims functions unused, but are required for generic - package appagent import ( @@ -28,12 +26,8 @@ import ( internal "github.com/bpfman/bpfman-operator/internal" gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" "github.com/google/uuid" - - v1 "k8s.io/api/core/v1" ) -//+kubebuilder:rbac:groups=bpfman.io,resources=tcxprograms,verbs=get;list;watch - // TcxProgramReconciler contains the info required to reconcile a TcxProgram type TcxProgramReconciler struct { ReconcilerCommon @@ -49,14 +43,6 @@ func (r *TcxProgramReconciler) getProgType() internal.ProgramType { return internal.Tc } -func (r *TcxProgramReconciler) getNode() *v1.Node { - return r.ourNode -} - -func (r *TcxProgramReconciler) getBpfGlobalData() map[string][]byte { - return r.appCommon.GlobalData -} - func (r *TcxProgramReconciler) shouldAttach() bool { return r.currentAttachPoint.ShouldAttach } @@ -159,7 +145,7 @@ func (r *TcxProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDele index := r.findAttachPoint(attachPoint) if index != nil { // Attach point already exists, so set ShouldAttach to true. - r.currentProgramState.TCX.AttachPoints[*index].AttachInfoCommon.ShouldAttach = true + r.currentProgramState.TCX.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true } else { // Attach point doesn't exist, so add it. r.Logger.Info("Attach point doesn't exist. Adding it.") @@ -182,8 +168,9 @@ func (r *TcxProgramReconciler) findAttachPoint(attachInfoState bpfmaniov1alpha1. for i, a := range r.currentProgramState.TCX.AttachPoints { // attachInfoState is the same as a if the the following fields are the // same: IfName, ContainerPid, Priority, and Direction. - if a.IfName == attachInfoState.IfName && reflect.DeepEqual(a.ContainerPid, attachInfoState.ContainerPid) && - a.Priority == attachInfoState.Priority && a.Direction == attachInfoState.Direction { + if a.IfName == attachInfoState.IfName && a.Priority == attachInfoState.Priority && + a.Direction == attachInfoState.Direction && + reflect.DeepEqual(a.ContainerPid, attachInfoState.ContainerPid) { return &i } } @@ -201,7 +188,7 @@ func (r *TcxProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerSt "mapOwnerStatus", mapOwnerStatus) // Get existing ebpf state from bpfman. - loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, internal.Tc) + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) if err != nil { r.Logger.Error(err, "failed to list loaded bpfman programs") updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) @@ -280,9 +267,9 @@ func (r *TcxProgramReconciler) getExpectedAttachPoints(ctx context.Context, atta for i := range *containerInfo { container := (*containerInfo)[i] for _, iface := range interfaces { - containerPid := uint32(container.pid) + containerPid := int32(container.pid) attachPoint := bpfmaniov1alpha1.TcxAttachInfoState{ - AttachInfoCommon: bpfmaniov1alpha1.AttachInfoCommon{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ ShouldAttach: true, UUID: uuid.New().String(), AttachId: nil, @@ -300,7 +287,7 @@ func (r *TcxProgramReconciler) getExpectedAttachPoints(ctx context.Context, atta } else { for _, iface := range interfaces { attachPoint := bpfmaniov1alpha1.TcxAttachInfoState{ - AttachInfoCommon: bpfmaniov1alpha1.AttachInfoCommon{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ ShouldAttach: true, UUID: uuid.New().String(), AttachId: nil, diff --git a/controllers/app-agent/cl-tracepoint-program.go b/controllers/app-agent/cl-tracepoint-program.go new file mode 100644 index 000000000..0e68dfbde --- /dev/null +++ b/controllers/app-agent/cl-tracepoint-program.go @@ -0,0 +1,248 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package appagent + +import ( + "context" + "fmt" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/app-agent/internal" + internal "github.com/bpfman/bpfman-operator/internal" + gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" + "github.com/google/uuid" +) + +// TracepointProgramReconciler contains the info required to reconcile a TracepointProgram +type TracepointProgramReconciler struct { + ReconcilerCommon + ProgramReconcilerCommon + currentAttachPoint *bpfmaniov1alpha1.TracepointAttachInfoState +} + +func (r *TracepointProgramReconciler) getProgId() *uint32 { + return r.currentProgramState.ProgramId +} + +func (r *TracepointProgramReconciler) getProgType() internal.ProgramType { + return internal.Tc +} + +func (r *TracepointProgramReconciler) shouldAttach() bool { + return r.currentAttachPoint.ShouldAttach +} + +func (r *TracepointProgramReconciler) getUUID() string { + return r.currentAttachPoint.UUID +} + +func (r *TracepointProgramReconciler) getAttachId() *uint32 { + return r.currentAttachPoint.AttachId +} + +func (r *TracepointProgramReconciler) setAttachId(id *uint32) { + r.currentAttachPoint.AttachId = id +} + +func (r *TracepointProgramReconciler) setAttachStatus(status bpfmaniov1alpha1.BpfProgramConditionType) bool { + return updateSimpleStatus(&r.currentAttachPoint.AttachStatus, status) +} + +func (r *TracepointProgramReconciler) getAttachStatus() bpfmaniov1alpha1.BpfProgramConditionType { + return r.currentAttachPoint.AttachStatus +} + +func (r *TracepointProgramReconciler) getLoadRequest(mapOwnerId *uint32) (*gobpfman.LoadRequest, error) { + + r.Logger.Info("Getting load request", "bpfFunctionName", r.currentProgram.BpfFunctionName, "reqAttachInfo", r.currentAttachPoint, "mapOwnerId", + mapOwnerId, "ByteCode", r.appCommon.ByteCode) + + bytecode, err := bpfmanagentinternal.GetBytecode(r.Client, &r.appCommon.ByteCode) + if err != nil { + return nil, fmt.Errorf("failed to process bytecode selector: %v", err) + } + + attachInfo := &gobpfman.TracepointAttachInfo{ + Tracepoint: r.currentAttachPoint.Name, + } + + // ANF-TODO: This is a temporary workaround for backwards compatibility. + // Fix it after old code removed. + var bpfFunctionName string + if r.currentProgram.BpfFunctionName != "" { + bpfFunctionName = r.currentProgram.BpfFunctionName + } else { + bpfFunctionName = r.currentProgram.Tracepoint.BpfFunctionName + } + + loadRequest := gobpfman.LoadRequest{ + Bytecode: bytecode, + Name: bpfFunctionName, + ProgramType: uint32(internal.Tc), + Attach: &gobpfman.AttachInfo{ + Info: &gobpfman.AttachInfo_TracepointAttachInfo{ + TracepointAttachInfo: attachInfo, + }, + }, + Metadata: map[string]string{internal.UuidMetadataKey: string(r.currentAttachPoint.UUID), internal.ProgramNameKey: "BpfApplication"}, + GlobalData: r.appCommon.GlobalData, + MapOwnerId: mapOwnerId, + } + + return &loadRequest, nil +} + +// updateAttachInfo processes the *ProgramInfo and updates the list of attach +// points contained in *AttachInfoState. +func (r *TracepointProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDeleted bool) error { + r.Logger.Info("Tracepoint updateAttachInfo()", "isBeingDeleted", isBeingDeleted) + + // Set ShouldAttach for all attach points in the node CRD to false. We'll + // update this in the next step for all attach points that are still + // present. + for i := range r.currentProgramState.Tracepoint.AttachPoints { + r.currentProgramState.Tracepoint.AttachPoints[i].ShouldAttach = false + } + + if isBeingDeleted { + // If the program is being deleted, we don't need to do anything else. + // + // ANF-TODO: When we have load/attach split, we shouldn't even need to + // set ShouldAttach to false above, because unloading the program should + // remove all attachments and updateAttachInfo won't be called. We + // probably should delete AttachPoints when unloading the program. + return nil + } + + for _, attachInfo := range r.currentProgram.Tracepoint.AttachPoints { + expectedAttachPoints, error := r.getExpectedAttachPoints(attachInfo) + if error != nil { + return fmt.Errorf("failed to get node attach points: %v", error) + } + for _, attachPoint := range expectedAttachPoints { + index := r.findAttachPoint(attachPoint) + if index != nil { + // Attach point already exists, so set ShouldAttach to true. + r.currentProgramState.Tracepoint.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true + } else { + // Attach point doesn't exist, so add it. + r.Logger.Info("Attach point doesn't exist. Adding it.") + r.currentProgramState.Tracepoint.AttachPoints = append(r.currentProgramState.Tracepoint.AttachPoints, attachPoint) + } + } + } + + // If any existing attach point is no longer on a list of expected attach + // points, ShouldAttach will remain set to false and it will get detached in + // a following step. + + return nil +} + +// ANF-TODO: Confirm what constitutes a match between two attach points. E.g., +// what if everything the same, but the priority and/or proceed_on values are +// different? +func (r *TracepointProgramReconciler) findAttachPoint(attachInfoState bpfmaniov1alpha1.TracepointAttachInfoState) *int { + for i, a := range r.currentProgramState.Tracepoint.AttachPoints { + // attachInfoState is the same as a if the the following fields are the + // same: IfName, ContainerPid, Priority, and Direction. + if a.Name == attachInfoState.Name { + return &i + } + } + return nil +} + +// processAttachInfo processes the attach points in *AttachInfoState. Based on +// the current state, it calls bpfman to attach or detach, or does nothing if +// the state is correct. It returns a boolean indicating if any changes were +// made. +// +// ANF-TODO: Generalize this function and move it into common. +func (r *TracepointProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerStatus *MapOwnerParamStatus) error { + r.Logger.Info("Processing attach info", "bpfFunctionName", r.currentProgram.Tracepoint.BpfFunctionName, + "mapOwnerStatus", mapOwnerStatus) + + // Get existing ebpf state from bpfman. + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) + if err != nil { + r.Logger.Error(err, "failed to list loaded bpfman programs") + updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) + return fmt.Errorf("failed to list loaded bpfman programs: %v", err) + } + + // The following map is used to keep track of attach points that need to be + // removed. If it's not empty at the end of the loop, we'll remove the + // attach points. + attachPointsToRemove := make(map[int]bool) + + var lastReconcileAttachmentError error = nil + for i := range r.currentProgramState.Tracepoint.AttachPoints { + r.currentAttachPoint = &r.currentProgramState.Tracepoint.AttachPoints[i] + remove, err := r.reconcileBpfAttachment(ctx, r, loadedBpfPrograms, mapOwnerStatus) + if err != nil { + r.Logger.Error(err, "failed to reconcile bpf attachment", "index", i) + // All errors are logged, but the last error is saved to return and + // we continue to process the rest of the attach points so errors + // don't block valid attach points. + lastReconcileAttachmentError = err + } + + if remove { + r.Logger.Info("Marking attach point for removal", "index", i) + attachPointsToRemove[i] = true + } + } + + if len(attachPointsToRemove) > 0 { + r.Logger.Info("Removing attach points", "attachPointsToRemove", attachPointsToRemove) + r.currentProgramState.Tracepoint.AttachPoints = r.removeAttachPoints(r.currentProgramState.Tracepoint.AttachPoints, attachPointsToRemove) + } + + return lastReconcileAttachmentError +} + +// removeAttachPoints removes attach points from a slice of attach points based on the keys in the map. +func (r *TracepointProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.TracepointAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.TracepointAttachInfoState { + var remainingAttachPoints []bpfmaniov1alpha1.TracepointAttachInfoState + for i, a := range attachPoints { + if _, ok := attachPointsToRemove[i]; !ok { + remainingAttachPoints = append(remainingAttachPoints, a) + } + } + return remainingAttachPoints +} + +// getExpectedAttachPoints expands *AttachInfo into a list of specific attach +// points. +func (r *TracepointProgramReconciler) getExpectedAttachPoints(attachInfo bpfmaniov1alpha1.TracepointAttachInfo, +) ([]bpfmaniov1alpha1.TracepointAttachInfoState, error) { + nodeAttachPoints := []bpfmaniov1alpha1.TracepointAttachInfoState{} + + attachPoint := bpfmaniov1alpha1.TracepointAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ + ShouldAttach: true, + UUID: uuid.New().String(), + AttachId: nil, + AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, + }, + Name: attachInfo.Name, + } + nodeAttachPoints = append(nodeAttachPoints, attachPoint) + + return nodeAttachPoints, nil +} diff --git a/controllers/app-agent/cl-uprobe-program.go b/controllers/app-agent/cl-uprobe-program.go new file mode 100644 index 000000000..d8c71682c --- /dev/null +++ b/controllers/app-agent/cl-uprobe-program.go @@ -0,0 +1,302 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package appagent + +import ( + "context" + "fmt" + "reflect" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/app-agent/internal" + internal "github.com/bpfman/bpfman-operator/internal" + gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" + "github.com/google/uuid" +) + +// UprobeProgramReconciler contains the info required to reconcile a UprobeProgram +type UprobeProgramReconciler struct { + ReconcilerCommon + ProgramReconcilerCommon + currentAttachPoint *bpfmaniov1alpha1.UprobeAttachInfoState +} + +func (r *UprobeProgramReconciler) getProgId() *uint32 { + return r.currentProgramState.ProgramId +} + +func (r *UprobeProgramReconciler) getProgType() internal.ProgramType { + return internal.Tc +} + +func (r *UprobeProgramReconciler) shouldAttach() bool { + return r.currentAttachPoint.ShouldAttach +} + +func (r *UprobeProgramReconciler) getUUID() string { + return r.currentAttachPoint.UUID +} + +func (r *UprobeProgramReconciler) getAttachId() *uint32 { + return r.currentAttachPoint.AttachId +} + +func (r *UprobeProgramReconciler) setAttachId(id *uint32) { + r.currentAttachPoint.AttachId = id +} + +func (r *UprobeProgramReconciler) setAttachStatus(status bpfmaniov1alpha1.BpfProgramConditionType) bool { + return updateSimpleStatus(&r.currentAttachPoint.AttachStatus, status) +} + +func (r *UprobeProgramReconciler) getAttachStatus() bpfmaniov1alpha1.BpfProgramConditionType { + return r.currentAttachPoint.AttachStatus +} + +func (r *UprobeProgramReconciler) getLoadRequest(mapOwnerId *uint32) (*gobpfman.LoadRequest, error) { + + r.Logger.Info("Getting load request", "bpfFunctionName", r.currentProgram.BpfFunctionName, "reqAttachInfo", r.currentAttachPoint, "mapOwnerId", + mapOwnerId, "ByteCode", r.appCommon.ByteCode) + + bytecode, err := bpfmanagentinternal.GetBytecode(r.Client, &r.appCommon.ByteCode) + if err != nil { + return nil, fmt.Errorf("failed to process bytecode selector: %v", err) + } + + attachInfo := &gobpfman.UprobeAttachInfo{ + FnName: &r.currentAttachPoint.FunctionName, + Offset: r.currentAttachPoint.Offset, + Target: r.currentAttachPoint.Target, + Retprobe: r.currentAttachPoint.RetProbe, + } + + if r.currentAttachPoint.ContainerPid != nil { + containerPid := int32(*r.currentAttachPoint.ContainerPid) + attachInfo.ContainerPid = &containerPid + } + + // ANF-TODO: This is a temporary workaround for backwards compatibility. + // Fix it after old code removed. + var bpfFunctionName string + if r.currentProgram.BpfFunctionName != "" { + bpfFunctionName = r.currentProgram.BpfFunctionName + } else { + bpfFunctionName = r.currentProgram.Uprobe.BpfFunctionName + } + + loadRequest := gobpfman.LoadRequest{ + Bytecode: bytecode, + Name: bpfFunctionName, + ProgramType: uint32(internal.Tc), + Attach: &gobpfman.AttachInfo{ + Info: &gobpfman.AttachInfo_UprobeAttachInfo{ + UprobeAttachInfo: attachInfo, + }, + }, + Metadata: map[string]string{internal.UuidMetadataKey: string(r.currentAttachPoint.UUID), internal.ProgramNameKey: "BpfApplication"}, + GlobalData: r.appCommon.GlobalData, + MapOwnerId: mapOwnerId, + } + + return &loadRequest, nil +} + +// updateAttachInfo processes the *ProgramInfo and updates the list of attach +// points contained in *AttachInfoState. +func (r *UprobeProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDeleted bool) error { + r.Logger.Info("Uprobe updateAttachInfo()", "isBeingDeleted", isBeingDeleted) + + // Set ShouldAttach for all attach points in the node CRD to false. We'll + // update this in the next step for all attach points that are still + // present. + for i := range r.currentProgramState.Uprobe.AttachPoints { + r.currentProgramState.Uprobe.AttachPoints[i].ShouldAttach = false + } + + if isBeingDeleted { + // If the program is being deleted, we don't need to do anything else. + // + // ANF-TODO: When we have load/attach split, we shouldn't even need to + // set ShouldAttach to false above, because unloading the program should + // remove all attachments and updateAttachInfo won't be called. We + // probably should delete AttachPoints when unloading the program. + return nil + } + + for _, attachInfo := range r.currentProgram.Uprobe.AttachPoints { + expectedAttachPoints, error := r.getExpectedAttachPoints(ctx, attachInfo) + if error != nil { + return fmt.Errorf("failed to get node attach points: %v", error) + } + for _, attachPoint := range expectedAttachPoints { + index := r.findAttachPoint(attachPoint) + if index != nil { + // Attach point already exists, so set ShouldAttach to true. + r.currentProgramState.Uprobe.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true + } else { + // Attach point doesn't exist, so add it. + r.Logger.Info("Attach point doesn't exist. Adding it.") + r.currentProgramState.Uprobe.AttachPoints = append(r.currentProgramState.Uprobe.AttachPoints, attachPoint) + } + } + } + + // If any existing attach point is no longer on a list of expected attach + // points, ShouldAttach will remain set to false and it will get detached in + // a following step. + + return nil +} + +// ANF-TODO: Confirm what constitutes a match between two attach points. E.g., +// what if everything the same, but the priority and/or proceed_on values are +// different? +func (r *UprobeProgramReconciler) findAttachPoint(attachInfoState bpfmaniov1alpha1.UprobeAttachInfoState) *int { + for i, a := range r.currentProgramState.Uprobe.AttachPoints { + // attachInfoState is the same as a if the the following fields are the + // same: IfName, ContainerPid, Priority, and Direction. + if a.FunctionName == attachInfoState.FunctionName && a.Offset == attachInfoState.Offset && + a.Target == attachInfoState.Target && a.RetProbe == attachInfoState.RetProbe && + reflect.DeepEqual(a.Pid, attachInfoState.Pid) && + reflect.DeepEqual(a.ContainerPid, attachInfoState.ContainerPid) { + return &i + } + } + return nil +} + +// processAttachInfo processes the attach points in *AttachInfoState. Based on +// the current state, it calls bpfman to attach or detach, or does nothing if +// the state is correct. It returns a boolean indicating if any changes were +// made. +// +// ANF-TODO: Generalize this function and move it into common. +func (r *UprobeProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerStatus *MapOwnerParamStatus) error { + r.Logger.Info("Processing attach info", "bpfFunctionName", r.currentProgram.Uprobe.BpfFunctionName, + "mapOwnerStatus", mapOwnerStatus) + + // Get existing ebpf state from bpfman. + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) + if err != nil { + r.Logger.Error(err, "failed to list loaded bpfman programs") + updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) + return fmt.Errorf("failed to list loaded bpfman programs: %v", err) + } + + // The following map is used to keep track of attach points that need to be + // removed. If it's not empty at the end of the loop, we'll remove the + // attach points. + attachPointsToRemove := make(map[int]bool) + + var lastReconcileAttachmentError error = nil + for i := range r.currentProgramState.Uprobe.AttachPoints { + r.currentAttachPoint = &r.currentProgramState.Uprobe.AttachPoints[i] + remove, err := r.reconcileBpfAttachment(ctx, r, loadedBpfPrograms, mapOwnerStatus) + if err != nil { + r.Logger.Error(err, "failed to reconcile bpf attachment", "index", i) + // All errors are logged, but the last error is saved to return and + // we continue to process the rest of the attach points so errors + // don't block valid attach points. + lastReconcileAttachmentError = err + } + + if remove { + r.Logger.Info("Marking attach point for removal", "index", i) + attachPointsToRemove[i] = true + } + } + + if len(attachPointsToRemove) > 0 { + r.Logger.Info("Removing attach points", "attachPointsToRemove", attachPointsToRemove) + r.currentProgramState.Uprobe.AttachPoints = r.removeAttachPoints(r.currentProgramState.Uprobe.AttachPoints, attachPointsToRemove) + } + + return lastReconcileAttachmentError +} + +// removeAttachPoints removes attach points from a slice of attach points based on the keys in the map. +func (r *UprobeProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.UprobeAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.UprobeAttachInfoState { + var remainingAttachPoints []bpfmaniov1alpha1.UprobeAttachInfoState + for i, a := range attachPoints { + if _, ok := attachPointsToRemove[i]; !ok { + remainingAttachPoints = append(remainingAttachPoints, a) + } + } + return remainingAttachPoints +} + +// getExpectedAttachPoints expands *AttachInfo into a list of specific attach +// points. +func (r *UprobeProgramReconciler) getExpectedAttachPoints(ctx context.Context, attachInfo bpfmaniov1alpha1.UprobeAttachInfo, +) ([]bpfmaniov1alpha1.UprobeAttachInfoState, error) { + nodeAttachPoints := []bpfmaniov1alpha1.UprobeAttachInfoState{} + + if attachInfo.Containers != nil { + // There is a container selector, so see if there are any matching + // containers on this node. + containerInfo, err := r.Containers.GetContainers( + ctx, + attachInfo.Containers.Namespace, + attachInfo.Containers.Pods, + attachInfo.Containers.ContainerNames, + r.Logger, + ) + if err != nil { + return nil, fmt.Errorf("failed to get container pids: %v", err) + } + + if containerInfo != nil && len(*containerInfo) != 0 { + // Containers were found, so create attach points. + for i := range *containerInfo { + container := (*containerInfo)[i] + containerPid := int32(container.pid) + attachPoint := bpfmaniov1alpha1.UprobeAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ + ShouldAttach: true, + UUID: uuid.New().String(), + AttachId: nil, + AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, + }, + FunctionName: attachInfo.FunctionName, + Offset: attachInfo.Offset, + Target: attachInfo.Target, + RetProbe: attachInfo.RetProbe, + Pid: attachInfo.Pid, + ContainerPid: &containerPid, + } + nodeAttachPoints = append(nodeAttachPoints, attachPoint) + } + } + } else { + attachPoint := bpfmaniov1alpha1.UprobeAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ + ShouldAttach: true, + UUID: uuid.New().String(), + AttachId: nil, + AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, + }, + FunctionName: attachInfo.FunctionName, + Offset: attachInfo.Offset, + Target: attachInfo.Target, + RetProbe: attachInfo.RetProbe, + Pid: attachInfo.Pid, + } + nodeAttachPoints = append(nodeAttachPoints, attachPoint) + } + + return nodeAttachPoints, nil +} diff --git a/controllers/app-agent/cl-xdp-program.go b/controllers/app-agent/cl-xdp-program.go index 0edcee673..3438d030c 100644 --- a/controllers/app-agent/cl-xdp-program.go +++ b/controllers/app-agent/cl-xdp-program.go @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -//lint:file-ignore U1000 Linter claims functions unused, but are required for generic - package appagent import ( @@ -28,8 +26,6 @@ import ( internal "github.com/bpfman/bpfman-operator/internal" gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" "github.com/google/uuid" - - v1 "k8s.io/api/core/v1" ) //+kubebuilder:rbac:groups=bpfman.io,resources=xdpprograms,verbs=get;list;watch @@ -49,14 +45,6 @@ func (r *XdpProgramReconciler) getProgType() internal.ProgramType { return internal.Xdp } -func (r *XdpProgramReconciler) getNode() *v1.Node { - return r.ourNode -} - -func (r *XdpProgramReconciler) getBpfGlobalData() map[string][]byte { - return r.appCommon.GlobalData -} - func (r *XdpProgramReconciler) shouldAttach() bool { return r.currentAttachPoint.ShouldAttach } @@ -183,7 +171,7 @@ func (r *XdpProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDele index := r.findAttachPoint(attachPoint) if index != nil { // Attach point already exists, so set ShouldAttach to true. - r.currentProgramState.XDP.AttachPoints[*index].AttachInfoCommon.ShouldAttach = true + r.currentProgramState.XDP.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true } else { // Attach point doesn't exist, so add it. r.currentProgramState.XDP.AttachPoints = append(r.currentProgramState.XDP.AttachPoints, attachPoint) @@ -224,7 +212,7 @@ func (r *XdpProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerSt "mapOwnerStatus", mapOwnerStatus) // Get existing ebpf state from bpfman. - loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, internal.Xdp) + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) if err != nil { r.Logger.Error(err, "failed to list loaded bpfman programs") updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) @@ -264,13 +252,13 @@ func (r *XdpProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerSt // removeAttachPoints removes attach points from a slice of attach points based on the keys in the map. func (r *XdpProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.XdpAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.XdpAttachInfoState { - var newAttachPoints []bpfmaniov1alpha1.XdpAttachInfoState + var remainingAttachPoints []bpfmaniov1alpha1.XdpAttachInfoState for i, a := range attachPoints { if _, ok := attachPointsToRemove[i]; !ok { - newAttachPoints = append(newAttachPoints, a) + remainingAttachPoints = append(remainingAttachPoints, a) } } - return newAttachPoints + return remainingAttachPoints } // getExpectedAttachPoints expands *AttachInfo into a list of specific attach @@ -303,9 +291,9 @@ func (r *XdpProgramReconciler) getExpectedAttachPoints(ctx context.Context, atta for i := range *containerInfo { container := (*containerInfo)[i] for _, iface := range interfaces { - containerPid := uint32(container.pid) + containerPid := int32(container.pid) attachPoint := bpfmaniov1alpha1.XdpAttachInfoState{ - AttachInfoCommon: bpfmaniov1alpha1.AttachInfoCommon{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ ShouldAttach: true, UUID: uuid.New().String(), AttachId: nil, @@ -323,7 +311,7 @@ func (r *XdpProgramReconciler) getExpectedAttachPoints(ctx context.Context, atta } else { for _, iface := range interfaces { attachPoint := bpfmaniov1alpha1.XdpAttachInfoState{ - AttachInfoCommon: bpfmaniov1alpha1.AttachInfoCommon{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ ShouldAttach: true, UUID: uuid.New().String(), AttachId: nil, diff --git a/controllers/app-agent/common.go b/controllers/app-agent/common.go index 359ea8c05..7df3f339f 100644 --- a/controllers/app-agent/common.go +++ b/controllers/app-agent/common.go @@ -36,6 +36,7 @@ import ( bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/app-agent/internal" + "github.com/bpfman/bpfman-operator/internal" gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" "github.com/go-logr/logr" "github.com/google/uuid" @@ -51,7 +52,7 @@ import ( // +kubebuilder:rbac:groups=core,resources=secrets,verbs=get const ( - retryDurationAgent = 5 * time.Second + retryDurationAgent = 1 * time.Second ) type ReconcilerCommon struct { @@ -94,6 +95,7 @@ type ApplicationReconciler interface { type ProgramReconciler interface { getLoadRequest(mapOwnerId *uint32) (*gobpfman.LoadRequest, error) getProgId() *uint32 + getProgType() internal.ProgramType updateAttachInfo(ctx context.Context, isBeingDeleted bool) error processAttachInfo(ctx context.Context, mapOwnerStatus *MapOwnerParamStatus) error shouldAttach() bool @@ -155,7 +157,7 @@ func (r *ReconcilerCommon) updateStatus( ctx context.Context, rec ApplicationReconciler, condition bpfmaniov1alpha1.ProgramConditionType, -) bool { +) (bool, error) { status := rec.GetStatus() r.Logger.V(1).Info("updateStatus()", "existing conds", status.Conditions, "new cond", condition) @@ -165,7 +167,7 @@ func (r *ReconcilerCommon) updateStatus( if numConditions == 1 { if status.Conditions[0].Type == string(condition) { // No change, so just return false -- not updated - return false + return false, nil } else { // We're changing the condition, so delete this one. The // new condition will be added below. @@ -182,12 +184,13 @@ func (r *ReconcilerCommon) updateStatus( } r.Logger.Info("Calling KubeAPI to update BpfAppState condition", "Name", rec.getAppStateName, "condition", condition.Condition("").Type) - if err := rec.updateBpfAppStatus(ctx, condition.Condition("")); err != nil { - r.Logger.Error(err, "failed to set BpfApplication object status") + err := rec.updateBpfAppStatus(ctx, condition.Condition("")) + if err != nil { + r.Logger.Info("failed to set BpfApplication object status", "reason", err) } r.Logger.V(1).Info("condition updated", "new condition", condition, "existing conds", status.Conditions) - return true + return true, err } // reconcileProgram is a common function for reconciling programs contained in a diff --git a/controllers/app-agent/ns-application-program.go b/controllers/app-agent/ns-application-program.go index abd1ff2da..d23925515 100644 --- a/controllers/app-agent/ns-application-program.go +++ b/controllers/app-agent/ns-application-program.go @@ -192,7 +192,7 @@ func (r *BpfNsApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Req // can't load the code. r.Logger.Error(err, "failed to reconcileLoad") objectChanged, _ := r.updateBpfAppStateSpec(ctx, bpfAppStateOriginal, bpfAppStateNew) - statusChanged := r.updateStatus(ctx, r, bpfmaniov1alpha1.ProgramReconcileError) + statusChanged, _ := r.updateStatus(ctx, r, bpfmaniov1alpha1.ProgramReconcileError) if statusChanged || objectChanged { return ctrl.Result{Requeue: true, RequeueAfter: retryDurationAgent}, nil } else { @@ -233,8 +233,27 @@ func (r *BpfNsApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Req switch prog.Type { // ANF-TODO: Implement support for other program types. - // case bpfmaniov1alpha1.ProgTypeUprobe: - // case bpfmaniov1alpha1.ProgTypeTC: + case bpfmaniov1alpha1.ProgTypeUprobe: + rec = &UprobeNsProgramReconciler{ + ReconcilerCommon: r.ReconcilerCommon, + ProgramNsReconcilerCommon: ProgramNsReconcilerCommon{ + appCommon: r.currentApp.Spec.BpfAppCommon, + currentProgram: prog, + currentProgramState: progState, + namespace: r.currentApp.Namespace, + }, + } + + case bpfmaniov1alpha1.ProgTypeTC: + rec = &TcNsProgramReconciler{ + ReconcilerCommon: r.ReconcilerCommon, + ProgramNsReconcilerCommon: ProgramNsReconcilerCommon{ + appCommon: r.currentApp.Spec.BpfAppCommon, + currentProgram: prog, + currentProgramState: progState, + namespace: r.currentApp.Namespace, + }, + } case bpfmaniov1alpha1.ProgTypeTCX: rec = &TcxNsProgramReconciler{ @@ -290,7 +309,12 @@ func (r *BpfNsApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{Requeue: true, RequeueAfter: retryDurationAgent}, nil } - statusChanged := r.updateStatus(ctx, r, bpfApplicationStatus) + statusChanged, err := r.updateStatus(ctx, r, bpfApplicationStatus) + if err != nil { + // This can happen if the object hasn't been updated in the API + // server yet, so we'll requeue. + return ctrl.Result{Requeue: true, RequeueAfter: retryDurationAgent}, nil + } if specChanged || statusChanged { r.Logger.Info("BpfNsApplicationState updated", "Name", r.currentAppState.Name, "Spec Changed", @@ -494,19 +518,25 @@ func (r *BpfNsApplicationReconciler) initializeNodeProgramList(bpfAppState *bpfm } switch prog.Type { case bpfmaniov1alpha1.ProgTypeTC: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) + progState.TC = &bpfmaniov1alpha1.TcNsProgramInfoState{ + AttachPoints: []bpfmaniov1alpha1.TcNsAttachInfoState{}, + } + case bpfmaniov1alpha1.ProgTypeTCX: progState.TCX = &bpfmaniov1alpha1.TcxNsProgramInfoState{ - AttachPoints: []bpfmaniov1alpha1.TcxAttachInfoState{}, + AttachPoints: []bpfmaniov1alpha1.TcxNsAttachInfoState{}, } + case bpfmaniov1alpha1.ProgTypeUprobe: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) - case bpfmaniov1alpha1.ProgTypeUretprobe: - panic(fmt.Sprintf("%v not implemented yet", prog.Type)) + progState.Uprobe = &bpfmaniov1alpha1.UprobeNsProgramInfoState{ + AttachPoints: []bpfmaniov1alpha1.UprobeNsAttachInfoState{}, + } + case bpfmaniov1alpha1.ProgTypeXDP: progState.XDP = &bpfmaniov1alpha1.XdpNsProgramInfoState{ AttachPoints: []bpfmaniov1alpha1.XdpAttachInfoState{}, } + default: panic(fmt.Sprintf("unexpected EBPFProgType: %#v", prog.Type)) } diff --git a/controllers/app-agent/ns-application-program_test.go b/controllers/app-agent/ns-application-program_test.go index ffe1334b9..7687cfbfb 100644 --- a/controllers/app-agent/ns-application-program_test.go +++ b/controllers/app-agent/ns-application-program_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package appagent import ( @@ -24,43 +40,53 @@ import ( func TestBpfNsApplicationControllerCreate(t *testing.T) { var ( // global config - appProgramName = "fakeAppProgram" - namespace = "bpfman" - bytecodePath = "/tmp/hello.o" - xdpBpfFunctionName = "XdpTest" - tcxBpfFunctionName = "TcxTest" - priority = 50 - fakeNode = testutils.NewNode("fake-control-plane") - fakeInt0 = "eth0" + appProgramName = "fakeAppProgram" + namespace = "bpfman" + bytecodePath = "/tmp/hello.o" + + xdpBpfFunctionName = "XdpTest" + tcxBpfFunctionName = "TcxTest" + uprobeBpfFunctionName = "UprobeTest" + tcBpfFunctionName = "TcTest" + + direction = "ingress" + AttachName = "AttachNameTest" + priority = 50 + fakeNode = testutils.NewNode("fake-control-plane") + fakeInt0 = "eth0" // fakeInt1 = "eth1" fakePodName = "my-pod" fakeContainerName = "my-container-1" - fakePid = int64(4490) + fakePid = int32(4490) ctx = context.TODO() ) programs := []bpfmaniov1alpha1.BpfNsApplicationProgram{} + fakeContainers := bpfmaniov1alpha1.ContainerNsSelector{ + Pods: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "test", + }, + }, + } + fakeInts := []string{fakeInt0} interfaceSelector := bpfmaniov1alpha1.InterfaceSelector{ Interfaces: &fakeInts, } + // Keep XDP as the first program because there's a test that assumes it's at programs[0]. proceedOn := []bpfmaniov1alpha1.XdpProceedOnValue{bpfmaniov1alpha1.XdpProceedOnValue("pass"), bpfmaniov1alpha1.XdpProceedOnValue("dispatcher_return")} xdpAttachInfo := bpfmaniov1alpha1.XdpNsAttachInfo{ InterfaceSelector: interfaceSelector, - Containers: bpfmaniov1alpha1.ContainerNsSelector{ - Pods: metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app": "test", - }, - }, - }, Priority: int32(priority), - ProceedOn: proceedOn, + Containers: fakeContainers, + Priority: int32(priority), + ProceedOn: proceedOn, } xdpProgram := bpfmaniov1alpha1.BpfNsApplicationProgram{ @@ -77,16 +103,11 @@ func TestBpfNsApplicationControllerCreate(t *testing.T) { tcxAttachInfo := bpfmaniov1alpha1.TcxNsAttachInfo{ InterfaceSelector: interfaceSelector, - Containers: bpfmaniov1alpha1.ContainerNsSelector{ - Pods: metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app": "test", - }, - }, - }, Direction: "ingress", - Priority: int32(priority), + Containers: fakeContainers, + Direction: "ingress", + Priority: int32(priority), } - tcProgram := bpfmaniov1alpha1.BpfNsApplicationProgram{ + tcxProgram := bpfmaniov1alpha1.BpfNsApplicationProgram{ BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ BpfFunctionName: tcxBpfFunctionName, }, @@ -95,6 +116,47 @@ func TestBpfNsApplicationControllerCreate(t *testing.T) { AttachPoints: []bpfmaniov1alpha1.TcxNsAttachInfo{tcxAttachInfo}, }, } + programs = append(programs, tcxProgram) + + uprobeProgram := bpfmaniov1alpha1.BpfNsApplicationProgram{ + BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ + BpfFunctionName: uprobeBpfFunctionName, + }, + Type: bpfmaniov1alpha1.ProgTypeUprobe, + Uprobe: &bpfmaniov1alpha1.UprobeNsProgramInfo{ + AttachPoints: []bpfmaniov1alpha1.UprobeNsAttachInfo{ + { + FunctionName: AttachName, + Offset: 200, + Target: "/bin/bash", + RetProbe: false, + Pid: &fakePid, + Containers: fakeContainers, + }, + }, + }, + } + programs = append(programs, uprobeProgram) + + tcProceedOn := []bpfmaniov1alpha1.TcProceedOnValue{bpfmaniov1alpha1.TcProceedOnValue("ok"), + bpfmaniov1alpha1.TcProceedOnValue("shot")} + + tcAttachInfo := bpfmaniov1alpha1.TcNsAttachInfo{ + InterfaceSelector: interfaceSelector, + Direction: direction, + Priority: int32(priority), + Containers: fakeContainers, + ProceedOn: tcProceedOn, + } + tcProgram := bpfmaniov1alpha1.BpfNsApplicationProgram{ + BpfProgramCommon: bpfmaniov1alpha1.BpfProgramCommon{ + BpfFunctionName: tcBpfFunctionName, + }, + Type: bpfmaniov1alpha1.ProgTypeTC, + TC: &bpfmaniov1alpha1.TcNsProgramInfo{ + AttachPoints: []bpfmaniov1alpha1.TcNsAttachInfo{tcAttachInfo}, + }, + } programs = append(programs, tcProgram) bpfApp := &bpfmaniov1alpha1.BpfNsApplication{ @@ -134,7 +196,7 @@ func TestBpfNsApplicationControllerCreate(t *testing.T) { { podName: fakePodName, containerName: fakeContainerName, - pid: fakePid, + pid: int64(fakePid), }, }, } diff --git a/controllers/app-agent/ns-tc-program.go b/controllers/app-agent/ns-tc-program.go new file mode 100644 index 000000000..f6f23ae0f --- /dev/null +++ b/controllers/app-agent/ns-tc-program.go @@ -0,0 +1,289 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package appagent + +import ( + "context" + "fmt" + "reflect" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/app-agent/internal" + internal "github.com/bpfman/bpfman-operator/internal" + gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" + "github.com/google/uuid" +) + +// TcNsProgramReconciler contains the info required to reconcile a TcNsProgram +type TcNsProgramReconciler struct { + ReconcilerCommon + ProgramNsReconcilerCommon + currentAttachPoint *bpfmaniov1alpha1.TcNsAttachInfoState +} + +func (r *TcNsProgramReconciler) getProgId() *uint32 { + return r.currentProgramState.ProgramId +} + +func (r *TcNsProgramReconciler) getProgType() internal.ProgramType { + return internal.Tc +} + +func (r *TcNsProgramReconciler) shouldAttach() bool { + return r.currentAttachPoint.ShouldAttach +} + +func (r *TcNsProgramReconciler) getUUID() string { + return r.currentAttachPoint.UUID +} + +func (r *TcNsProgramReconciler) getAttachId() *uint32 { + return r.currentAttachPoint.AttachId +} + +func (r *TcNsProgramReconciler) setAttachId(id *uint32) { + r.currentAttachPoint.AttachId = id +} + +func (r *TcNsProgramReconciler) setAttachStatus(status bpfmaniov1alpha1.BpfProgramConditionType) bool { + return updateSimpleStatus(&r.currentAttachPoint.AttachStatus, status) +} + +func (r *TcNsProgramReconciler) getAttachStatus() bpfmaniov1alpha1.BpfProgramConditionType { + return r.currentAttachPoint.AttachStatus +} + +func (r *TcNsProgramReconciler) getLoadRequest(mapOwnerId *uint32) (*gobpfman.LoadRequest, error) { + + r.Logger.Info("Getting load request", "bpfFunctionName", r.currentProgram.BpfFunctionName, "reqAttachInfo", r.currentAttachPoint, "mapOwnerId", + mapOwnerId, "ByteCode", r.appCommon.ByteCode) + + bytecode, err := bpfmanagentinternal.GetBytecode(r.Client, &r.appCommon.ByteCode) + if err != nil { + return nil, fmt.Errorf("failed to process bytecode selector: %v", err) + } + + attachInfo := &gobpfman.TCAttachInfo{ + Priority: r.currentAttachPoint.Priority, + Iface: r.currentAttachPoint.IfName, + Direction: r.currentAttachPoint.Direction, + ProceedOn: tcProceedOnToInt(r.currentAttachPoint.ProceedOn), + } + + netns := fmt.Sprintf("/host/proc/%d/ns/net", r.currentAttachPoint.ContainerPid) + attachInfo.Netns = &netns + + // ANF-TODO: This is a temporary workaround for backwards compatibility. + // Fix it after old code removed. + var bpfFunctionName string + if r.currentProgram.BpfFunctionName != "" { + bpfFunctionName = r.currentProgram.BpfFunctionName + } else { + bpfFunctionName = r.currentProgram.TC.BpfFunctionName + } + + loadRequest := gobpfman.LoadRequest{ + Bytecode: bytecode, + Name: bpfFunctionName, + ProgramType: uint32(internal.Tc), + Attach: &gobpfman.AttachInfo{ + Info: &gobpfman.AttachInfo_TcAttachInfo{ + TcAttachInfo: attachInfo, + }, + }, + Metadata: map[string]string{internal.UuidMetadataKey: string(r.currentAttachPoint.UUID), internal.ProgramNameKey: "BpfApplication"}, + GlobalData: r.appCommon.GlobalData, + MapOwnerId: mapOwnerId, + } + + return &loadRequest, nil +} + +// updateAttachInfo processes the *ProgramInfo and updates the list of attach +// points contained in *AttachInfoState. +func (r *TcNsProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDeleted bool) error { + r.Logger.Info("TC updateAttachInfo()", "isBeingDeleted", isBeingDeleted) + + // Set ShouldAttach for all attach points in the node CRD to false. We'll + // update this in the next step for all attach points that are still + // present. + for i := range r.currentProgramState.TC.AttachPoints { + r.currentProgramState.TC.AttachPoints[i].ShouldAttach = false + } + + if isBeingDeleted { + // If the program is being deleted, we don't need to do anything else. + // + // ANF-TODO: When we have load/attach split, we shouldn't even need to + // set ShouldAttach to false above, because unloading the program should + // remove all attachments and updateAttachInfo won't be called. We + // probably should delete AttachPoints when unloading the program. + return nil + } + + for _, attachInfo := range r.currentProgram.TC.AttachPoints { + expectedAttachPoints, error := r.getExpectedAttachPoints(ctx, attachInfo) + if error != nil { + return fmt.Errorf("failed to get node attach points: %v", error) + } + for _, attachPoint := range expectedAttachPoints { + index := r.findAttachPoint(attachPoint) + if index != nil { + // Attach point already exists, so set ShouldAttach to true. + r.currentProgramState.TC.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true + } else { + // Attach point doesn't exist, so add it. + r.Logger.Info("Attach point doesn't exist. Adding it.") + r.currentProgramState.TC.AttachPoints = append(r.currentProgramState.TC.AttachPoints, attachPoint) + } + } + } + + // If any existing attach point is no longer on a list of expected attach + // points, ShouldAttach will remain set to false and it will get detached in + // a following step. + + return nil +} + +// ANF-TODO: Confirm what constitutes a match between two attach points. E.g., +// what if everything the same, but the priority and/or proceed_on values are +// different? +func (r *TcNsProgramReconciler) findAttachPoint(attachInfoState bpfmaniov1alpha1.TcNsAttachInfoState) *int { + for i, a := range r.currentProgramState.TC.AttachPoints { + // attachInfoState is the same as a if the the following fields are the + // same: IfName, ContainerPid, Priority, and ProceedOn. + if a.IfName == attachInfoState.IfName && a.Direction == attachInfoState.Direction && + a.Priority == attachInfoState.Priority && + reflect.DeepEqual(a.ContainerPid, attachInfoState.ContainerPid) && + reflect.DeepEqual(a.ProceedOn, attachInfoState.ProceedOn) { + return &i + } + } + return nil +} + +// processAttachInfo processes the attach points in *AttachInfoState. Based on +// the current state, it calls bpfman to attach or detach, or does nothing if +// the state is correct. It returns a boolean indicating if any changes were +// made. +// +// ANF-TODO: Generalize this function and move it into common. +func (r *TcNsProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerStatus *MapOwnerParamStatus) error { + r.Logger.Info("Processing attach info", "bpfFunctionName", r.currentProgram.TC.BpfFunctionName, + "mapOwnerStatus", mapOwnerStatus) + + // Get existing ebpf state from bpfman. + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) + if err != nil { + r.Logger.Error(err, "failed to list loaded bpfman programs") + updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) + return fmt.Errorf("failed to list loaded bpfman programs: %v", err) + } + + // The following map is used to keep track of attach points that need to be + // removed. If it's not empty at the end of the loop, we'll remove the + // attach points. + attachPointsToRemove := make(map[int]bool) + + var lastReconcileAttachmentError error = nil + for i := range r.currentProgramState.TC.AttachPoints { + r.currentAttachPoint = &r.currentProgramState.TC.AttachPoints[i] + remove, err := r.reconcileBpfAttachment(ctx, r, loadedBpfPrograms, mapOwnerStatus) + if err != nil { + r.Logger.Error(err, "failed to reconcile bpf attachment", "index", i) + // All errors are logged, but the last error is saved to return and + // we continue to process the rest of the attach points so errors + // don't block valid attach points. + lastReconcileAttachmentError = err + } + + if remove { + r.Logger.Info("Marking attach point for removal", "index", i) + attachPointsToRemove[i] = true + } + } + + if len(attachPointsToRemove) > 0 { + r.Logger.Info("Removing attach points", "attachPointsToRemove", attachPointsToRemove) + r.currentProgramState.TC.AttachPoints = r.removeAttachPoints(r.currentProgramState.TC.AttachPoints, attachPointsToRemove) + } + + return lastReconcileAttachmentError +} + +// removeAttachPoints removes attach points from a slice of attach points based on the keys in the map. +func (r *TcNsProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.TcNsAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.TcNsAttachInfoState { + var remainingAttachPoints []bpfmaniov1alpha1.TcNsAttachInfoState + for i, a := range attachPoints { + if _, ok := attachPointsToRemove[i]; !ok { + remainingAttachPoints = append(remainingAttachPoints, a) + } + } + return remainingAttachPoints +} + +// getExpectedAttachPoints expands *AttachInfo into a list of specific attach +// points. +func (r *TcNsProgramReconciler) getExpectedAttachPoints(ctx context.Context, attachInfo bpfmaniov1alpha1.TcNsAttachInfo, +) ([]bpfmaniov1alpha1.TcNsAttachInfoState, error) { + interfaces, err := getInterfaces(&attachInfo.InterfaceSelector, r.ourNode) + if err != nil { + return nil, fmt.Errorf("failed to get interfaces for TcProgram: %v", err) + } + + nodeAttachPoints := []bpfmaniov1alpha1.TcNsAttachInfoState{} + + // There is a container selector, so see if there are any matching + // containers on this node. + containerInfo, err := r.Containers.GetContainers( + ctx, + r.namespace, + attachInfo.Containers.Pods, + attachInfo.Containers.ContainerNames, + r.Logger, + ) + if err != nil { + return nil, fmt.Errorf("failed to get container pids: %v", err) + } + + if containerInfo != nil && len(*containerInfo) != 0 { + // Containers were found, so create attach points. + for i := range *containerInfo { + container := (*containerInfo)[i] + for _, iface := range interfaces { + containerPid := int32(container.pid) + attachPoint := bpfmaniov1alpha1.TcNsAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ + ShouldAttach: true, + UUID: uuid.New().String(), + AttachId: nil, + AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, + }, + IfName: iface, + ContainerPid: containerPid, + Priority: attachInfo.Priority, + Direction: attachInfo.Direction, + ProceedOn: attachInfo.ProceedOn, + } + nodeAttachPoints = append(nodeAttachPoints, attachPoint) + } + } + } + + return nodeAttachPoints, nil +} diff --git a/controllers/app-agent/ns-tcx-program.go b/controllers/app-agent/ns-tcx-program.go index 7a5a6657c..94a206068 100644 --- a/controllers/app-agent/ns-tcx-program.go +++ b/controllers/app-agent/ns-tcx-program.go @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -//lint:file-ignore U1000 Linter claims functions unused, but are required for generic - package appagent import ( @@ -28,17 +26,13 @@ import ( internal "github.com/bpfman/bpfman-operator/internal" gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" "github.com/google/uuid" - - v1 "k8s.io/api/core/v1" ) -//+kubebuilder:rbac:groups=bpfman.io,resources=tcxprograms,verbs=get;list;watch - // TcxNsProgramReconciler contains the info required to reconcile a TcxNsProgram type TcxNsProgramReconciler struct { ReconcilerCommon ProgramNsReconcilerCommon - currentAttachPoint *bpfmaniov1alpha1.TcxAttachInfoState + currentAttachPoint *bpfmaniov1alpha1.TcxNsAttachInfoState } func (r *TcxNsProgramReconciler) getProgId() *uint32 { @@ -49,14 +43,6 @@ func (r *TcxNsProgramReconciler) getProgType() internal.ProgramType { return internal.Tc } -func (r *TcxNsProgramReconciler) getNode() *v1.Node { - return r.ourNode -} - -func (r *TcxNsProgramReconciler) getBpfGlobalData() map[string][]byte { - return r.appCommon.GlobalData -} - func (r *TcxNsProgramReconciler) shouldAttach() bool { return r.currentAttachPoint.ShouldAttach } @@ -101,10 +87,8 @@ func (r *TcxNsProgramReconciler) getLoadRequest(mapOwnerId *uint32) (*gobpfman.L Direction: r.currentAttachPoint.Direction, } - if r.currentAttachPoint.ContainerPid != nil { - netns := fmt.Sprintf("/host/proc/%d/ns/net", *r.currentAttachPoint.ContainerPid) - attachInfo.Netns = &netns - } + netns := fmt.Sprintf("/host/proc/%d/ns/net", r.currentAttachPoint.ContainerPid) + attachInfo.Netns = &netns // ANF-TODO: This is a temporary workaround for backwards compatibility. // Fix it after old code removed. @@ -163,7 +147,7 @@ func (r *TcxNsProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDe index := r.findAttachPoint(attachPoint) if index != nil { // Attach point already exists, so set ShouldAttach to true. - r.currentProgramState.TCX.AttachPoints[*index].AttachInfoCommon.ShouldAttach = true + r.currentProgramState.TCX.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true } else { // Attach point doesn't exist, so add it. r.Logger.Info("Attach point doesn't exist. Adding it.") @@ -182,7 +166,7 @@ func (r *TcxNsProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDe // ANF-TODO: Confirm what constitutes a match between two attach points. E.g., // what if everything the same, but the priority and/or proceed_on values are // different? -func (r *TcxNsProgramReconciler) findAttachPoint(attachInfoState bpfmaniov1alpha1.TcxAttachInfoState) *int { +func (r *TcxNsProgramReconciler) findAttachPoint(attachInfoState bpfmaniov1alpha1.TcxNsAttachInfoState) *int { for i, a := range r.currentProgramState.TCX.AttachPoints { // attachInfoState is the same as a if the the following fields are the // same: IfName, ContainerPid, Priority, and Direction. @@ -205,7 +189,7 @@ func (r *TcxNsProgramReconciler) processAttachInfo(ctx context.Context, mapOwner "mapOwnerStatus", mapOwnerStatus) // Get existing ebpf state from bpfman. - loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, internal.Tc) + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) if err != nil { r.Logger.Error(err, "failed to list loaded bpfman programs") updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) @@ -244,8 +228,8 @@ func (r *TcxNsProgramReconciler) processAttachInfo(ctx context.Context, mapOwner } // removeAttachPoints removes attach points from a slice of attach points based on the keys in the map. -func (r *TcxNsProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.TcxAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.TcxAttachInfoState { - var remainingAttachPoints []bpfmaniov1alpha1.TcxAttachInfoState +func (r *TcxNsProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.TcxNsAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.TcxNsAttachInfoState { + var remainingAttachPoints []bpfmaniov1alpha1.TcxNsAttachInfoState for i, a := range attachPoints { if _, ok := attachPointsToRemove[i]; !ok { remainingAttachPoints = append(remainingAttachPoints, a) @@ -257,13 +241,13 @@ func (r *TcxNsProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alp // getExpectedAttachPoints expands *AttachInfo into a list of specific attach // points. func (r *TcxNsProgramReconciler) getExpectedAttachPoints(ctx context.Context, attachInfo bpfmaniov1alpha1.TcxNsAttachInfo, -) ([]bpfmaniov1alpha1.TcxAttachInfoState, error) { +) ([]bpfmaniov1alpha1.TcxNsAttachInfoState, error) { interfaces, err := getInterfaces(&attachInfo.InterfaceSelector, r.ourNode) if err != nil { return nil, fmt.Errorf("failed to get interfaces for TcxNsProgram: %v", err) } - nodeAttachPoints := []bpfmaniov1alpha1.TcxAttachInfoState{} + nodeAttachPoints := []bpfmaniov1alpha1.TcxNsAttachInfoState{} containerInfo, err := r.Containers.GetContainers( ctx, @@ -281,16 +265,16 @@ func (r *TcxNsProgramReconciler) getExpectedAttachPoints(ctx context.Context, at for i := range *containerInfo { container := (*containerInfo)[i] for _, iface := range interfaces { - containerPid := uint32(container.pid) - attachPoint := bpfmaniov1alpha1.TcxAttachInfoState{ - AttachInfoCommon: bpfmaniov1alpha1.AttachInfoCommon{ + containerPid := int32(container.pid) + attachPoint := bpfmaniov1alpha1.TcxNsAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ ShouldAttach: true, UUID: uuid.New().String(), AttachId: nil, AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, }, IfName: iface, - ContainerPid: &containerPid, + ContainerPid: containerPid, Priority: attachInfo.Priority, Direction: attachInfo.Direction, } diff --git a/controllers/app-agent/ns-uprobe-program.go b/controllers/app-agent/ns-uprobe-program.go new file mode 100644 index 000000000..1b4f89b1b --- /dev/null +++ b/controllers/app-agent/ns-uprobe-program.go @@ -0,0 +1,282 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package appagent + +import ( + "context" + "fmt" + "reflect" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/app-agent/internal" + internal "github.com/bpfman/bpfman-operator/internal" + gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" + "github.com/google/uuid" +) + +// UprobeNsProgramReconciler contains the info required to reconcile a UprobeNsProgram +type UprobeNsProgramReconciler struct { + ReconcilerCommon + ProgramNsReconcilerCommon + currentAttachPoint *bpfmaniov1alpha1.UprobeNsAttachInfoState +} + +func (r *UprobeNsProgramReconciler) getProgId() *uint32 { + return r.currentProgramState.ProgramId +} + +func (r *UprobeNsProgramReconciler) getProgType() internal.ProgramType { + return internal.Tc +} + +func (r *UprobeNsProgramReconciler) shouldAttach() bool { + return r.currentAttachPoint.ShouldAttach +} + +func (r *UprobeNsProgramReconciler) getUUID() string { + return r.currentAttachPoint.UUID +} + +func (r *UprobeNsProgramReconciler) getAttachId() *uint32 { + return r.currentAttachPoint.AttachId +} + +func (r *UprobeNsProgramReconciler) setAttachId(id *uint32) { + r.currentAttachPoint.AttachId = id +} + +func (r *UprobeNsProgramReconciler) setAttachStatus(status bpfmaniov1alpha1.BpfProgramConditionType) bool { + return updateSimpleStatus(&r.currentAttachPoint.AttachStatus, status) +} + +func (r *UprobeNsProgramReconciler) getAttachStatus() bpfmaniov1alpha1.BpfProgramConditionType { + return r.currentAttachPoint.AttachStatus +} + +func (r *UprobeNsProgramReconciler) getLoadRequest(mapOwnerId *uint32) (*gobpfman.LoadRequest, error) { + + r.Logger.Info("Getting load request", "bpfFunctionName", r.currentProgram.BpfFunctionName, "reqAttachInfo", r.currentAttachPoint, "mapOwnerId", + mapOwnerId, "ByteCode", r.appCommon.ByteCode) + + bytecode, err := bpfmanagentinternal.GetBytecode(r.Client, &r.appCommon.ByteCode) + if err != nil { + return nil, fmt.Errorf("failed to process bytecode selector: %v", err) + } + + attachInfo := &gobpfman.UprobeAttachInfo{ + FnName: &r.currentAttachPoint.FunctionName, + Offset: r.currentAttachPoint.Offset, + Target: r.currentAttachPoint.Target, + Retprobe: r.currentAttachPoint.RetProbe, + } + + containerPid := int32(r.currentAttachPoint.ContainerPid) + attachInfo.ContainerPid = &containerPid + + // ANF-TODO: This is a temporary workaround for backwards compatibility. + // Fix it after old code removed. + var bpfFunctionName string + if r.currentProgram.BpfFunctionName != "" { + bpfFunctionName = r.currentProgram.BpfFunctionName + } else { + bpfFunctionName = r.currentProgram.Uprobe.BpfFunctionName + } + + loadRequest := gobpfman.LoadRequest{ + Bytecode: bytecode, + Name: bpfFunctionName, + ProgramType: uint32(internal.Tc), + Attach: &gobpfman.AttachInfo{ + Info: &gobpfman.AttachInfo_UprobeAttachInfo{ + UprobeAttachInfo: attachInfo, + }, + }, + Metadata: map[string]string{internal.UuidMetadataKey: string(r.currentAttachPoint.UUID), internal.ProgramNameKey: "BpfApplication"}, + GlobalData: r.appCommon.GlobalData, + MapOwnerId: mapOwnerId, + } + + return &loadRequest, nil +} + +// updateAttachInfo processes the *ProgramInfo and updates the list of attach +// points contained in *AttachInfoState. +func (r *UprobeNsProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDeleted bool) error { + r.Logger.Info("Uprobe updateAttachInfo()", "isBeingDeleted", isBeingDeleted) + + // Set ShouldAttach for all attach points in the node CRD to false. We'll + // update this in the next step for all attach points that are still + // present. + for i := range r.currentProgramState.Uprobe.AttachPoints { + r.currentProgramState.Uprobe.AttachPoints[i].ShouldAttach = false + } + + if isBeingDeleted { + // If the program is being deleted, we don't need to do anything else. + // + // ANF-TODO: When we have load/attach split, we shouldn't even need to + // set ShouldAttach to false above, because unloading the program should + // remove all attachments and updateAttachInfo won't be called. We + // probably should delete AttachPoints when unloading the program. + return nil + } + + for _, attachInfo := range r.currentProgram.Uprobe.AttachPoints { + expectedAttachPoints, error := r.getExpectedAttachPoints(ctx, attachInfo) + if error != nil { + return fmt.Errorf("failed to get node attach points: %v", error) + } + for _, attachPoint := range expectedAttachPoints { + index := r.findAttachPoint(attachPoint) + if index != nil { + // Attach point already exists, so set ShouldAttach to true. + r.currentProgramState.Uprobe.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true + } else { + // Attach point doesn't exist, so add it. + r.Logger.Info("Attach point doesn't exist. Adding it.") + r.currentProgramState.Uprobe.AttachPoints = append(r.currentProgramState.Uprobe.AttachPoints, attachPoint) + } + } + } + + // If any existing attach point is no longer on a list of expected attach + // points, ShouldAttach will remain set to false and it will get detached in + // a following step. + + return nil +} + +// ANF-TODO: Confirm what constitutes a match between two attach points. E.g., +// what if everything the same, but the priority and/or proceed_on values are +// different? +func (r *UprobeNsProgramReconciler) findAttachPoint(attachInfoState bpfmaniov1alpha1.UprobeNsAttachInfoState) *int { + for i, a := range r.currentProgramState.Uprobe.AttachPoints { + // attachInfoState is the same as a if the the following fields are the + // same: IfName, ContainerPid, Priority, and Direction. + if a.FunctionName == attachInfoState.FunctionName && a.Offset == attachInfoState.Offset && + a.Target == attachInfoState.Target && a.RetProbe == attachInfoState.RetProbe && + reflect.DeepEqual(a.Pid, attachInfoState.Pid) && + reflect.DeepEqual(a.ContainerPid, attachInfoState.ContainerPid) { + return &i + } + } + return nil +} + +// processAttachInfo processes the attach points in *AttachInfoState. Based on +// the current state, it calls bpfman to attach or detach, or does nothing if +// the state is correct. It returns a boolean indicating if any changes were +// made. +// +// ANF-TODO: Generalize this function and move it into common. +func (r *UprobeNsProgramReconciler) processAttachInfo(ctx context.Context, mapOwnerStatus *MapOwnerParamStatus) error { + r.Logger.Info("Processing attach info", "bpfFunctionName", r.currentProgram.Uprobe.BpfFunctionName, + "mapOwnerStatus", mapOwnerStatus) + + // Get existing ebpf state from bpfman. + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) + if err != nil { + r.Logger.Error(err, "failed to list loaded bpfman programs") + updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) + return fmt.Errorf("failed to list loaded bpfman programs: %v", err) + } + + // The following map is used to keep track of attach points that need to be + // removed. If it's not empty at the end of the loop, we'll remove the + // attach points. + attachPointsToRemove := make(map[int]bool) + + var lastReconcileAttachmentError error = nil + for i := range r.currentProgramState.Uprobe.AttachPoints { + r.currentAttachPoint = &r.currentProgramState.Uprobe.AttachPoints[i] + remove, err := r.reconcileBpfAttachment(ctx, r, loadedBpfPrograms, mapOwnerStatus) + if err != nil { + r.Logger.Error(err, "failed to reconcile bpf attachment", "index", i) + // All errors are logged, but the last error is saved to return and + // we continue to process the rest of the attach points so errors + // don't block valid attach points. + lastReconcileAttachmentError = err + } + + if remove { + r.Logger.Info("Marking attach point for removal", "index", i) + attachPointsToRemove[i] = true + } + } + + if len(attachPointsToRemove) > 0 { + r.Logger.Info("Removing attach points", "attachPointsToRemove", attachPointsToRemove) + r.currentProgramState.Uprobe.AttachPoints = r.removeAttachPoints(r.currentProgramState.Uprobe.AttachPoints, attachPointsToRemove) + } + + return lastReconcileAttachmentError +} + +// removeAttachPoints removes attach points from a slice of attach points based on the keys in the map. +func (r *UprobeNsProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.UprobeNsAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.UprobeNsAttachInfoState { + var remainingAttachPoints []bpfmaniov1alpha1.UprobeNsAttachInfoState + for i, a := range attachPoints { + if _, ok := attachPointsToRemove[i]; !ok { + remainingAttachPoints = append(remainingAttachPoints, a) + } + } + return remainingAttachPoints +} + +// getExpectedAttachPoints expands *AttachInfo into a list of specific attach +// points. +func (r *UprobeNsProgramReconciler) getExpectedAttachPoints(ctx context.Context, attachInfo bpfmaniov1alpha1.UprobeNsAttachInfo, +) ([]bpfmaniov1alpha1.UprobeNsAttachInfoState, error) { + nodeAttachPoints := []bpfmaniov1alpha1.UprobeNsAttachInfoState{} + + // See if there are any matching containers on this node. + containerInfo, err := r.Containers.GetContainers( + ctx, + r.namespace, + attachInfo.Containers.Pods, + attachInfo.Containers.ContainerNames, + r.Logger, + ) + if err != nil { + return nil, fmt.Errorf("failed to get container pids: %v", err) + } + + if containerInfo != nil && len(*containerInfo) != 0 { + // Containers were found, so create attach points. + for i := range *containerInfo { + container := (*containerInfo)[i] + containerPid := int32(container.pid) + attachPoint := bpfmaniov1alpha1.UprobeNsAttachInfoState{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ + ShouldAttach: true, + UUID: uuid.New().String(), + AttachId: nil, + AttachStatus: bpfmaniov1alpha1.BpfProgCondNotAttached, + }, + FunctionName: attachInfo.FunctionName, + Offset: attachInfo.Offset, + Target: attachInfo.Target, + RetProbe: attachInfo.RetProbe, + Pid: attachInfo.Pid, + ContainerPid: containerPid, + } + nodeAttachPoints = append(nodeAttachPoints, attachPoint) + } + } + + return nodeAttachPoints, nil +} diff --git a/controllers/app-agent/ns-xdp-program.go b/controllers/app-agent/ns-xdp-program.go index e1a9ca15a..79fa6cfd9 100644 --- a/controllers/app-agent/ns-xdp-program.go +++ b/controllers/app-agent/ns-xdp-program.go @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -//lint:file-ignore U1000 Linter claims functions unused, but are required for generic - package appagent import ( @@ -28,8 +26,6 @@ import ( internal "github.com/bpfman/bpfman-operator/internal" gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" "github.com/google/uuid" - - v1 "k8s.io/api/core/v1" ) //+kubebuilder:rbac:groups=bpfman.io,resources=xdpprograms,verbs=get;list;watch @@ -49,14 +45,6 @@ func (r *XdpNsProgramReconciler) getProgType() internal.ProgramType { return internal.Xdp } -func (r *XdpNsProgramReconciler) getNode() *v1.Node { - return r.ourNode -} - -func (r *XdpNsProgramReconciler) getBpfGlobalData() map[string][]byte { - return r.appCommon.GlobalData -} - func (r *XdpNsProgramReconciler) shouldAttach() bool { return r.currentAttachPoint.ShouldAttach } @@ -163,7 +151,7 @@ func (r *XdpNsProgramReconciler) updateAttachInfo(ctx context.Context, isBeingDe index := r.findAttachPoint(attachPoint) if index != nil { // Attach point already exists, so set ShouldAttach to true. - r.currentProgramState.XDP.AttachPoints[*index].AttachInfoCommon.ShouldAttach = true + r.currentProgramState.XDP.AttachPoints[*index].AttachInfoStateCommon.ShouldAttach = true } else { // Attach point doesn't exist, so add it. r.currentProgramState.XDP.AttachPoints = append(r.currentProgramState.XDP.AttachPoints, attachPoint) @@ -204,7 +192,7 @@ func (r *XdpNsProgramReconciler) processAttachInfo(ctx context.Context, mapOwner "mapOwnerStatus", mapOwnerStatus) // Get existing ebpf state from bpfman. - loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, internal.Xdp) + loadedBpfPrograms, err := bpfmanagentinternal.ListBpfmanPrograms(ctx, r.BpfmanClient, r.getProgType()) if err != nil { r.Logger.Error(err, "failed to list loaded bpfman programs") updateSimpleStatus(&r.currentProgramState.ProgramAttachStatus, bpfmaniov1alpha1.BpfProgCondAttachError) @@ -244,13 +232,13 @@ func (r *XdpNsProgramReconciler) processAttachInfo(ctx context.Context, mapOwner // removeAttachPoints removes attach points from a slice of attach points based on the keys in the map. func (r *XdpNsProgramReconciler) removeAttachPoints(attachPoints []bpfmaniov1alpha1.XdpAttachInfoState, attachPointsToRemove map[int]bool) []bpfmaniov1alpha1.XdpAttachInfoState { - var newAttachPoints []bpfmaniov1alpha1.XdpAttachInfoState + var remainingAttachPoints []bpfmaniov1alpha1.XdpAttachInfoState for i, a := range attachPoints { if _, ok := attachPointsToRemove[i]; !ok { - newAttachPoints = append(newAttachPoints, a) + remainingAttachPoints = append(remainingAttachPoints, a) } } - return newAttachPoints + return remainingAttachPoints } // getExpectedAttachPoints expands *AttachInfo into a list of specific attach @@ -280,9 +268,9 @@ func (r *XdpNsProgramReconciler) getExpectedAttachPoints(ctx context.Context, at for i := range *containerInfo { container := (*containerInfo)[i] for _, iface := range interfaces { - containerPid := uint32(container.pid) + containerPid := int32(container.pid) attachPoint := bpfmaniov1alpha1.XdpAttachInfoState{ - AttachInfoCommon: bpfmaniov1alpha1.AttachInfoCommon{ + AttachInfoStateCommon: bpfmaniov1alpha1.AttachInfoStateCommon{ ShouldAttach: true, UUID: uuid.New().String(), AttachId: nil, diff --git a/controllers/bpfman-agent/application-ns-program.go b/controllers/bpfman-agent/application-ns-program.go index 709f5d4d4..3beb06947 100644 --- a/controllers/bpfman-agent/application-ns-program.go +++ b/controllers/bpfman-agent/application-ns-program.go @@ -93,8 +93,7 @@ func (r *BpfNsApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Req var appProgramMap = make(map[string]bool) for j, p := range a.Spec.Programs { switch p.Type { - case bpfmaniov1alpha1.ProgTypeUprobe, - bpfmaniov1alpha1.ProgTypeUretprobe: + case bpfmaniov1alpha1.ProgTypeUprobe: appProgramId := fmt.Sprintf("%s-%s-%s", strings.ToLower(string(p.Type)), sanitize(p.Uprobe.AttachPoints[0].FunctionName), p.Uprobe.BpfFunctionName) uprobeProgram := bpfmaniov1alpha1.UprobeNsProgram{ ObjectMeta: metav1.ObjectMeta{ diff --git a/controllers/bpfman-agent/application-program.go b/controllers/bpfman-agent/application-program.go index 4d1d88484..8a72b1f02 100644 --- a/controllers/bpfman-agent/application-program.go +++ b/controllers/bpfman-agent/application-program.go @@ -138,8 +138,7 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque complete, res, err = r.reconcileCommon(ctx, rec, fexitObjects) lastRec = rec - case bpfmaniov1alpha1.ProgTypeKprobe, - bpfmaniov1alpha1.ProgTypeKretprobe: + case bpfmaniov1alpha1.ProgTypeKprobe: appProgramId := fmt.Sprintf("%s-%s-%s", strings.ToLower(string(p.Type)), sanitize(p.Kprobe.AttachPoints[0].FunctionName), p.Kprobe.BpfFunctionName) kprobeProgram := bpfmaniov1alpha1.KprobeProgram{ ObjectMeta: metav1.ObjectMeta{ @@ -162,8 +161,7 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque complete, res, err = r.reconcileCommon(ctx, rec, kprobeObjects) lastRec = rec - case bpfmaniov1alpha1.ProgTypeUprobe, - bpfmaniov1alpha1.ProgTypeUretprobe: + case bpfmaniov1alpha1.ProgTypeUprobe: appProgramId := fmt.Sprintf("%s-%s-%s", strings.ToLower(string(p.Type)), sanitize(p.Uprobe.AttachPoints[0].FunctionName), p.Uprobe.BpfFunctionName) uprobeProgram := bpfmaniov1alpha1.UprobeProgram{ ObjectMeta: metav1.ObjectMeta{