-
Notifications
You must be signed in to change notification settings - Fork 658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MapIterator support for Queues/Stacks + further testing #1349
Open
s41m0n
wants to merge
2
commits into
cilium:main
Choose a base branch
from
s41m0n:feature/queue_stack_support
base: main
Could not load branches
Branch not found: {{ refName }}
Could not load tags
Nothing to show
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Binary file not shown.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// This program demonstrates attaching an eBPF program to a network interface | ||
// with XDP (eXpress Data Path). The program parses the IPv4 source address | ||
// from packets and pushes the address alongside the computed packet arrival timestamp | ||
// into a Queue. This is just an example and probably does not represent the most | ||
// efficient way to perform such a task. Another potential solution would be to use | ||
// an HashMap with a small __u64 arrays associated to each IPv4 address (key). | ||
// In both the two ways it is possible to lose some packet if (a) queue is not large | ||
// enough or the packet processing time is slow or (b) if the associated array is | ||
// smaller than the actual received packet from an address. | ||
// The userspace program (Go code in this file) prints the contents | ||
// of the map to stdout every second, parsing the raw structure into a human-readable | ||
// IPv4 address and Unix timestamp. | ||
// It is possible to modify the XDP program to drop or redirect packets | ||
// as well -- give it a try! | ||
// This example depends on bpf_link, available in Linux kernel version 5.7 or newer. | ||
package main | ||
|
||
import ( | ||
"encoding/binary" | ||
"fmt" | ||
"log" | ||
"net" | ||
"net/netip" | ||
"os" | ||
"strings" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/cilium/ebpf" | ||
"github.com/cilium/ebpf/link" | ||
) | ||
|
||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go bpf xdp.c -- -I../headers | ||
|
||
func main() { | ||
if len(os.Args) < 2 { | ||
log.Fatalf("Please specify a network interface") | ||
} | ||
|
||
// Look up the network interface by name. | ||
ifaceName := os.Args[1] | ||
iface, err := net.InterfaceByName(ifaceName) | ||
if err != nil { | ||
log.Fatalf("lookup network iface %q: %s", ifaceName, err) | ||
} | ||
|
||
// Load pre-compiled programs into the kernel. | ||
objs := bpfObjects{} | ||
if err := loadBpfObjects(&objs, nil); err != nil { | ||
log.Fatalf("loading objects: %s", err) | ||
} | ||
defer objs.Close() | ||
|
||
// Attach the program. | ||
l, err := link.AttachXDP(link.XDPOptions{ | ||
Program: objs.XdpProgFunc, | ||
Interface: iface.Index, | ||
}) | ||
if err != nil { | ||
log.Fatalf("could not attach XDP program: %s", err) | ||
} | ||
defer l.Close() | ||
|
||
log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) | ||
log.Printf("Press Ctrl-C to exit and remove the program") | ||
|
||
// Retrieve boot time once, and use it for every later ktime conversions | ||
bootTime := getSysBoot() | ||
|
||
// Print the contents of the BPF queue (packet source IP address and timestamp). | ||
ticker := time.NewTicker(1 * time.Second) | ||
defer ticker.Stop() | ||
for range ticker.C { | ||
s, err := formatMapContents(objs.QueueWithData, bootTime) | ||
if err != nil { | ||
log.Printf("Error reading map: %s", err) | ||
continue | ||
} | ||
log.Printf("Map contents:\n%s", s) | ||
} | ||
} | ||
|
||
// formatMapContents formats an output string with the content of the provided map. | ||
// | ||
// For each entry, the function outputs a line containing the human-readable IPv4 address | ||
// retrieved from the packet structure formatted and the converted ktime_ns into Unix Time | ||
// | ||
// In case of error or empty map, the function returns the corresponding error. | ||
func formatMapContents(m *ebpf.Map, bootTime time.Time) (string, error) { | ||
var ( | ||
sb strings.Builder | ||
val bpfPacketData | ||
) | ||
iter := m.Iterate() | ||
for iter.Next(nil, &val) { | ||
// Convert the __u32 into human-readable IPv4 | ||
a4 := [4]byte{} | ||
binary.LittleEndian.PutUint32(a4[:], val.SrcIp) | ||
addr := netip.AddrFrom4(a4) | ||
|
||
// Convert ktime timestamp into Time struct, adding the retrieved | ||
// timestamp to the previously computer boot time | ||
t := bootTime.Add(time.Duration(val.Timestamp) * time.Nanosecond) | ||
|
||
sb.WriteString(fmt.Sprintf("\t%s - %s\n", addr, t)) | ||
} | ||
return sb.String(), iter.Err() | ||
} | ||
|
||
// Retrieve system boot time and convert it into Time struct | ||
func getSysBoot() time.Time { | ||
sysInfo := &syscall.Sysinfo_t{} | ||
syscall.Sysinfo(sysInfo) | ||
return time.Now().Add(-time.Duration(sysInfo.Uptime) * time.Second) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like you mention yourself, this isn't a prime example of queues or stacks. I understand you want an example to showcase those map types. A good example is small and doesn't add to much extra info. I honestly don't think it adds much to your PR, I suggest dropping it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your point and I agree.
Let me know if the
ktime -> Unix time
conversion utility function could be useful somehow. I'd leave it up to the user and wouldn't insert it as part of the library.