From 12e1973aa79c4d02672726d8a55ffbb3ee4593cb Mon Sep 17 00:00:00 2001 From: Hang Yan Date: Tue, 12 Nov 2024 17:47:30 +0800 Subject: [PATCH] Fix captured packets file unrecognized issue on osx (#6795) By default, gopacket will write snap length=0 in the pcapng file header, means unlimited snaplen. tcpdump on osx(libpcap version 1.10.1) cannot recognize this and will report error. This patch will set a default value(524288) for it. Signed-off-by: Hang Yan --- .../packetcapture/packetcapture_controller.go | 9 +- test/e2e/packetcapture_test.go | 103 ++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/pkg/agent/packetcapture/packetcapture_controller.go b/pkg/agent/packetcapture/packetcapture_controller.go index 3691f0cd895..93d15e4875d 100644 --- a/pkg/agent/packetcapture/packetcapture_controller.go +++ b/pkg/agent/packetcapture/packetcapture_controller.go @@ -449,7 +449,14 @@ func (c *Controller) performCapture( if err != nil { return false, err } - pcapngWriter, err := pcapgo.NewNgWriter(file, layers.LinkTypeEthernet) + + // set SnapLength here to make tcpdump on Mac OSX works. By default, its value is + // 0 and means unlimited, but tcpdump on Mac OSX will complain: + // 'tcpdump: pcap_loop: invalid packet capture length , bigger than snaplen of 524288' + defaultNgInterface := pcapgo.DefaultNgInterface + defaultNgInterface.SnapLength = 524288 + defaultNgInterface.LinkType = layers.LinkTypeEthernet + pcapngWriter, err := pcapgo.NewNgWriterInterface(file, defaultNgInterface, pcapgo.DefaultNgWriterOptions) if err != nil { return false, fmt.Errorf("couldn't initialize a pcap writer: %w", err) } diff --git a/test/e2e/packetcapture_test.go b/test/e2e/packetcapture_test.go index c6b8c77cc4a..29a94ac11fa 100644 --- a/test/e2e/packetcapture_test.go +++ b/test/e2e/packetcapture_test.go @@ -17,12 +17,19 @@ package e2e import ( "context" "fmt" + "io" "net" + "os" + "path/filepath" "sort" "strings" "testing" "time" + "github.com/gopacket/gopacket" + "github.com/gopacket/gopacket/layers" + "github.com/gopacket/gopacket/pcapgo" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" @@ -524,6 +531,19 @@ func runPacketCaptureTest(t *testing.T, data *TestData, tc pcTestCase) { require.NoError(t, err) } } + var srcPodIPs *PodIPs + if tc.pc.Spec.Source.IP != nil { + ip := net.ParseIP(*tc.pc.Spec.Source.IP) + srcPodIPs = &PodIPs{IPv4: &ip} + } else if tc.pc.Spec.Source.Pod != nil { + pod, err := data.clientset.CoreV1().Pods(tc.pc.Spec.Source.Pod.Namespace).Get(context.TODO(), tc.pc.Spec.Source.Pod.Name, metav1.GetOptions{}) + if err != nil { + require.True(t, errors.IsNotFound(err)) + } else { + srcPodIPs, err = parsePodIPs(pod) + require.NoError(t, err) + } + } if _, err := data.crdClient.CrdV1alpha1().PacketCaptures().Create(context.TODO(), tc.pc, metav1.CreateOptions{}); err != nil { t.Fatalf("Error when creating PacketCapture: %v", err) @@ -586,6 +606,25 @@ func runPacketCaptureTest(t *testing.T, data *TestData, tc pcTestCase) { if !packetCaptureStatusEqual(pc.Status, tc.expectedStatus) { t.Errorf("CR status not match, actual: %+v, expected: %+v", pc.Status, tc.expectedStatus) } + + if tc.pc.Spec.FileServer == nil || tc.expectedStatus.NumberCaptured == 0 { + return + } + // verify packets. + antreaPodName, err := data.getAntreaPodOnNode(nodeName(0)) + require.NoError(t, err) + fileName := fmt.Sprintf("%s.pcapng", tc.pc.Name) + dstFileName := filepath.Join(os.TempDir(), fileName) + packetFile := filepath.Join("/tmp", "antrea", "packetcapture", "packets", fileName) + err = data.copyPodFiles(antreaPodName, "antrea-agent", "kube-system", packetFile, os.TempDir()) + require.NoError(t, err) + file, err := os.Open(dstFileName) + require.NoError(t, err) + defer file.Close() + err = verifyPacketFile(t, tc.pc, file, tc.expectedStatus.NumberCaptured, *srcPodIPs.IPv4, *dstPodIPs.IPv4) + require.NoError(t, err) + err = os.Remove(dstFileName) + require.NoError(t, err) } func (data *TestData) waitForPacketCapture(t *testing.T, name string, specTimeout int, fn func(*crdv1alpha1.PacketCapture) bool) (*crdv1alpha1.PacketCapture, error) { @@ -670,3 +709,67 @@ func conditionSliceEqualsIgnoreLastTransitionTime(as, bs []crdv1alpha1.PacketCap } return true } + +// verifyPacketFile will read the packets file and check if packet count and packet data match with CR. +func verifyPacketFile(t *testing.T, pc *crdv1alpha1.PacketCapture, reader io.Reader, targetNum int32, srcIP net.IP, dstIP net.IP) (err error) { + ngReader, err := pcapgo.NewNgReader(reader, pcapgo.DefaultNgReaderOptions) + if err != nil { + return err + } + + for i := int32(0); i < targetNum; i++ { + data, _, err := ngReader.ReadPacketData() + if err != nil { + return err + } + packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default) + ipLayer := packet.Layer(layers.LayerTypeIPv4) + assert.NotNil(t, ipLayer) + ip, _ := ipLayer.(*layers.IPv4) + assert.Equal(t, srcIP.String(), ip.SrcIP.String()) + assert.Equal(t, dstIP.String(), ip.DstIP.String()) + + if pc.Spec.Packet == nil { + return nil + } + + packetSpec := pc.Spec.Packet + proto := packetSpec.Protocol + if proto == nil { + return nil + } + if strings.ToUpper(proto.StrVal) == "TCP" || proto.IntVal == 6 { + tcpLayer := packet.Layer(layers.LayerTypeTCP) + assert.NotNil(t, tcpLayer) + tcp, _ := tcpLayer.(*layers.TCP) + if packetSpec.TransportHeader.TCP != nil { + ports := packetSpec.TransportHeader.TCP + if ports.DstPort != nil { + assert.Equal(t, *ports.DstPort, int32(tcp.DstPort)) + } + if ports.SrcPort != nil { + assert.Equal(t, *ports.SrcPort, int32(tcp.SrcPort)) + } + } + } + if strings.ToUpper(proto.StrVal) == "UDP" || proto.IntVal == 17 { + udpLayer := packet.Layer(layers.LayerTypeUDP) + assert.NotNil(t, udpLayer) + udp, _ := udpLayer.(*layers.UDP) + if packetSpec.TransportHeader.UDP != nil { + ports := packetSpec.TransportHeader.UDP + if ports.DstPort != nil { + assert.Equal(t, *ports.DstPort, int32(udp.DstPort)) + } + if ports.SrcPort != nil { + assert.Equal(t, *ports.SrcPort, int32(udp.SrcPort)) + } + } + } + if strings.ToUpper(proto.StrVal) == "ICMP" || proto.IntVal == 1 { + icmpLayer := packet.Layer(layers.LayerTypeICMPv4) + assert.NotNil(t, icmpLayer) + } + } + return nil +}