From 6b4c0bc7ddc2cf950a9487dc8a376a97cb33428c Mon Sep 17 00:00:00 2001 From: Grant Linville Date: Mon, 5 Feb 2024 20:22:53 -0500 Subject: [PATCH] feat: track egress network traffic (#11) Signed-off-by: Grant Linville --- aggregator/data.go | 13 +++--- aggregator/persist.go | 3 +- datastore/dto.go | 23 +++++++---- datastore/payload.go | 38 +++++++++-------- datastore/prometheus.go | 77 ++++++++++++++++++++++++++++------- datastore/server.go | 19 +++++++-- ebpf/throughput/bpf_bpfeb.o | Bin 5064 -> 5112 bytes ebpf/throughput/bpf_bpfel.o | Bin 5064 -> 5112 bytes ebpf/throughput/main.go | 64 ++++++++++++++++++++++++++--- ebpf/throughput/throughput.c | 9 +++- go.mod | 4 +- go.sum | 4 +- 12 files changed, 192 insertions(+), 62 deletions(-) diff --git a/aggregator/data.go b/aggregator/data.go index cf93f47..81c1276 100644 --- a/aggregator/data.go +++ b/aggregator/data.go @@ -516,12 +516,13 @@ func (a *Aggregator) processThroughputEvent(e throughput.ThroughputEvent) { } pkt := datastore.Packet{ - Time: e.Timestamp, - Size: e.Size, - FromIP: e.SAddr, - FromPort: e.SPort, - ToIP: e.DAddr, - ToPort: e.DPort, + Time: e.Timestamp, + Size: e.Size, + FromIP: e.SAddr, + FromPort: e.SPort, + ToIP: e.DAddr, + ToPort: e.DPort, + IsIngress: e.IsIngress, } // determine source information diff --git a/aggregator/persist.go b/aggregator/persist.go index d61ec37..801b8f7 100644 --- a/aggregator/persist.go +++ b/aggregator/persist.go @@ -45,7 +45,8 @@ func (a *Aggregator) processPod(d k8s.K8sResourceMessage) { OwnerID: ownerID, OwnerName: ownerName, - Labels: pod.GetLabels(), + Labels: pod.GetLabels(), + Annotations: pod.GetAnnotations(), } switch d.EventType { diff --git a/datastore/dto.go b/datastore/dto.go index 6a7bb4a..69750e2 100644 --- a/datastore/dto.go +++ b/datastore/dto.go @@ -1,15 +1,16 @@ package datastore type Pod struct { - UID string // Pod UID - Name string // Pod Name - Namespace string // Namespace - Image string // Main container image - IP string // Pod IP - OwnerType string // ReplicaSet or nil - OwnerID string // ReplicaSet UID - OwnerName string // ReplicaSet Name - Labels map[string]string + UID string // Pod UID + Name string // Pod Name + Namespace string // Namespace + Image string // Main container image + IP string // Pod IP + OwnerType string // ReplicaSet or nil + OwnerID string // ReplicaSet UID + OwnerName string // ReplicaSet Name + Labels map[string]string + Annotations map[string]string } type Service struct { @@ -152,4 +153,8 @@ type Packet struct { ToType Dest ToUID string ToPort uint16 + // IsIngress indicates whether the packet was detected on the ingress or egress bpf filter. + // The egress bpf filter is used for the throughput metric, while the ingress bpf filter + // is used for the egress metric. (Seems backwards, but that is how it actually works.) + IsIngress bool } diff --git a/datastore/payload.go b/datastore/payload.go index 4384af0..5b0fadf 100644 --- a/datastore/payload.go +++ b/datastore/payload.go @@ -25,15 +25,16 @@ type Event interface { } type PodEvent struct { - UID string `json:"uid"` - EventType string `json:"event_type"` - Name string `json:"name"` - Namespace string `json:"namespace"` - IP string `json:"ip"` - OwnerType string `json:"owner_type"` - OwnerName string `json:"owner_name"` - OwnerID string `json:"owner_id"` - Labels map[string]string `json:"labels"` + UID string `json:"uid"` + EventType string `json:"event_type"` + Name string `json:"name"` + Namespace string `json:"namespace"` + IP string `json:"ip"` + OwnerType string `json:"owner_type"` + OwnerName string `json:"owner_name"` + OwnerID string `json:"owner_id"` + Labels map[string]string `json:"labels"` + Annotations map[string]string `json:"annotations"` } func (p PodEvent) GetUID() string { return p.UID } @@ -136,15 +137,16 @@ type RequestsPayload struct { func convertPodToPodEvent(pod Pod, eventType string) PodEvent { return PodEvent{ - UID: pod.UID, - EventType: eventType, - Name: pod.Name, - Namespace: pod.Namespace, - IP: pod.IP, - OwnerType: pod.OwnerType, - OwnerName: pod.OwnerName, - OwnerID: pod.OwnerID, - Labels: pod.Labels, + UID: pod.UID, + EventType: eventType, + Name: pod.Name, + Namespace: pod.Namespace, + IP: pod.IP, + OwnerType: pod.OwnerType, + OwnerName: pod.OwnerName, + OwnerID: pod.OwnerID, + Labels: pod.Labels, + Annotations: pod.Annotations, } } diff --git a/datastore/prometheus.go b/datastore/prometheus.go index 7b09c47..11facd8 100644 --- a/datastore/prometheus.go +++ b/datastore/prometheus.go @@ -2,7 +2,9 @@ package datastore import ( "context" + "encoding/json" "strconv" + "strings" "sync" "github.com/ddosify/alaz/log" @@ -10,15 +12,20 @@ import ( ) const ( + accountIDLabel = "acorn.io/account-id" appLabel = "acorn.io/app-public-name" appNamespaceLabel = "acorn.io/app-namespace" containerLabel = "acorn.io/container-name" + projectLabel = "acorn.io/project-name" + + resolvedOfferingsAnnotation = "acorn.io/container-resolved-offerings" ) var ( latencyHistLabels = []string{"toPod", "toAcornApp", "toAcornContainer", "toAcornAppNamespace"} statusCounterLabels = []string{"toPod", "toAcornApp", "toAcornContainer", "toAcornAppNamespace", "status"} throughputCounterLabels = []string{"fromPod", "fromAcornApp", "fromAcornContainer", "fromAcornAppNamespace", "fromHostname", "toPod", "toAcornApp", "toAcornContainer", "toAcornAppNamespace", "toPort", "toHostname"} + egressCounterLabels = []string{"fromPod", "fromAcornApp", "fromAcornContainer", "fromAcornProject", "fromAcornAccountID", "fromAcornComputeClass"} ) type PrometheusExporter struct { @@ -28,6 +35,7 @@ type PrometheusExporter struct { latencyHistogram *prometheus.HistogramVec statusCounter *prometheus.CounterVec throughputCounter *prometheus.CounterVec + egressCounter *prometheus.CounterVec podCache *eventCache podIPCache *eventCache @@ -111,6 +119,15 @@ func NewPrometheusExporter(ctx context.Context) *PrometheusExporter { ) exporter.reg.MustRegister(exporter.throughputCounter) + exporter.egressCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: "alaz", + Name: "egress", + }, + egressCounterLabels, + ) + exporter.reg.MustRegister(exporter.egressCounter) + go exporter.handleReqs() go exporter.handlePackets() @@ -174,12 +191,9 @@ func (p *PrometheusExporter) handlePackets() { } func (p *PrometheusExporter) handlePacket(pkt Packet) { - labels := prometheus.Labels{ - "toPort": strconv.Itoa(int(pkt.ToPort)), - } - - // We only want to keep metrics between pods in the same project (app namespace) - if pkt.FromType == PodSource && pkt.ToType == PodDest { + // Check for packets between pods in the same project. + // (Reminder: !pkt.IsIngress means that the packet was detected by the egress eBPF filter.) + if !pkt.IsIngress && pkt.FromType == PodSource && pkt.ToType == PodDest { fromPod, found := p.podCache.get(pkt.FromUID) toPod, found2 := p.podCache.get(pkt.ToUID) @@ -188,19 +202,52 @@ func (p *PrometheusExporter) handlePacket(pkt Packet) { fromPod.(PodEvent).Labels[appNamespaceLabel] == toPod.(PodEvent).Labels[appNamespaceLabel] && fromPod.(PodEvent).Labels[appLabel] == toPod.(PodEvent).Labels[appLabel] { - labels["fromPod"] = fromPod.(PodEvent).Name - labels["fromAcornApp"] = fromPod.(PodEvent).Labels[appLabel] - labels["fromAcornAppNamespace"] = fromPod.(PodEvent).Labels[appNamespaceLabel] - labels["fromAcornContainer"] = fromPod.(PodEvent).Labels[containerLabel] - - labels["toPod"] = toPod.(PodEvent).Name - labels["toAcornApp"] = toPod.(PodEvent).Labels[appLabel] - labels["toAcornAppNamespace"] = toPod.(PodEvent).Labels[appNamespaceLabel] - labels["toAcornContainer"] = toPod.(PodEvent).Labels[containerLabel] + labels := prometheus.Labels{ + "toPort": strconv.Itoa(int(pkt.ToPort)), + "fromPod": fromPod.(PodEvent).Name, + "fromAcornApp": fromPod.(PodEvent).Labels[appLabel], + "fromAcornAppNamespace": fromPod.(PodEvent).Labels[appNamespaceLabel], + "fromAcornContainer": fromPod.(PodEvent).Labels[containerLabel], + "toPod": toPod.(PodEvent).Name, + "toAcornApp": toPod.(PodEvent).Labels[appLabel], + "toAcornAppNamespace": toPod.(PodEvent).Labels[appNamespaceLabel], + "toAcornContainer": toPod.(PodEvent).Labels[containerLabel], + } p.throughputCounter.With(setEmptyPrometheusLabels(labels, throughputCounterLabels)).Add(float64(pkt.Size)) } } + + // Check for packets from pods to outside the cluster. + // (Reminder: pkt.IsIngress just means that the packet was detected by the ingress eBPF filter, which is actually detecting egress traffic.) + // OutsideDest indicates that the destination IP address is not a known pod or service IP address. + // We also filter out the 10. prefix because that is the internal IP address range used by the cluster. + if pkt.IsIngress && pkt.FromType == PodSource && pkt.ToType == OutsideDest && !strings.HasPrefix(pkt.ToIP, "10.") { + fromPod, found := p.podCache.get(pkt.FromUID) + + if found && fromPod.(PodEvent).Labels[accountIDLabel] != "" { + labels := prometheus.Labels{ + "fromPod": fromPod.(PodEvent).Name, + "fromAcornApp": fromPod.(PodEvent).Labels[appLabel], + "fromAcornProject": fromPod.(PodEvent).Labels[projectLabel], + "fromAcornContainer": fromPod.(PodEvent).Labels[containerLabel], + "fromAcornAccountID": fromPod.(PodEvent).Labels[accountIDLabel], + } + + if resolvedOfferingsJson, ok := fromPod.(PodEvent).Annotations[resolvedOfferingsAnnotation]; ok { + offerings := map[string]any{} + if err := json.Unmarshal([]byte(resolvedOfferingsJson), &offerings); err == nil { + if class, ok := offerings["class"]; ok { + labels["fromAcornComputeClass"] = class.(string) + } + } else { + log.Logger.Error().Msg(err.Error()) + } + } + + p.egressCounter.With(setEmptyPrometheusLabels(labels, egressCounterLabels)).Add(float64(pkt.Size)) + } + } } func setEmptyPrometheusLabels(labels prometheus.Labels, labelList []string) prometheus.Labels { diff --git a/datastore/server.go b/datastore/server.go index b0dc983..6d56918 100644 --- a/datastore/server.go +++ b/datastore/server.go @@ -34,7 +34,7 @@ func NewServer(ctx context.Context, reg *prometheus.Registry, podIPCache *eventC } func (s *Server) Serve() { - http.Handle("/metricz", s.authorizePrometheus(promhttp.HandlerFor(s.reg, promhttp.HandlerOpts{}))) + http.Handle("/metricz", s.authorize(promhttp.HandlerFor(s.reg, promhttp.HandlerOpts{}))) go func() { if err := http.ListenAndServe(":8080", nil); err != nil { log.Logger.Error().Err(err).Msg("error while serving metrics") @@ -44,7 +44,10 @@ func (s *Server) Serve() { log.Logger.Info().Msg("Prometheus HTTP server stopped") } -func (s *Server) authorizePrometheus(handler http.Handler) http.Handler { +func (s *Server) authorize(handler http.Handler) http.Handler { + // Only two things are authorized to scrape Alaz: + // - Prometheus + // - cluster-agent return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var sourceIP string parts := strings.Split(r.RemoteAddr, ":") @@ -58,7 +61,7 @@ func (s *Server) authorizePrometheus(handler http.Handler) http.Handler { } pod, ok := s.podIPCache.get(sourceIP) - if ok && pod.(PodEvent).Namespace == s.prometheusNamespace { + if ok && (isPrometheus(pod.(PodEvent)) || isClusterAgent(pod.(PodEvent))) { handler.ServeHTTP(w, r) return } @@ -68,3 +71,13 @@ func (s *Server) authorizePrometheus(handler http.Handler) http.Handler { w.Write([]byte("401 Unauthorized\n")) }) } + +func isPrometheus(p PodEvent) bool { + return p.Namespace == "prometheus-operator" +} + +func isClusterAgent(p PodEvent) bool { + return p.Labels[appLabel] == "cluster-agent" && + p.Labels[appNamespaceLabel] == "acorn" && + p.Labels[containerLabel] == "cluster-agent" +} diff --git a/ebpf/throughput/bpf_bpfeb.o b/ebpf/throughput/bpf_bpfeb.o index 8efee0c55ccd53a55eabc3f5ceb7d6a764d17764..fb88850f3d87010a87e9fdc64e10b2da9e451871 100644 GIT binary patch delta 1444 zcmYk5O-xfk5XWa<`=q6`eJvj?U%Ca9M+~tN0|@dF5{(CpTuk%?l`k6%DOM8l8^go6=}L@yjL>culrOd3y3B;w5O7COn>+24QW?VFw5x7Ciz-lAFXrdWn&tQN!_89ufZ-!|ED6*vUHCI9t&8EWOmEMLD z^5dLFa9xc^57)T-o=d-<$}dod3&@nPT2N)z<5QcG)oy%M{Ivote zyxDlgly88n6wk@@`|-?Z5j43A^jN)FUZBf%p^oQc))rBwH1l*OXVw{Ze2m-4iJQ&c z@v->i$mlfh>f(wg$M1|LlY?5%l)F}0D2}Xj-o=kVd~m-LUyAS5{CXwJ;rb(G=<#8D z00xL#p8AyRDJtLb`3aDwM68_Hs04UT)#45g*@MgrxD+|6`4Yeqb`sh(-vwBVAops1 z0kB5z4;*o7jSlI$7Cr%NQ^IrsQ~aUkKX{CEXjr8zh?qLKq}b5`u*;D@YJLN-NAYdV zZvpm$$lne*1^X5XSuK3U)1!o(=HI|d0ysyBKIrrSbK?KR@Eu|YCEuB- z*0eO^>a&vevVF@w?Iv%goERH-@{)*~$X$)^lGM}0l1@wX(Dlr2J@|H_9qh`_xB({4 zzGpZeaqa0nt{x4Ok0?`4KrrY^(s63x5U@T9c&A~>dLm9utSc(ybyZsrl#g>1Iw!U2 z>xoe{&&J}^&U!`SbBHK()g*>vciG|E1I(pFrW2(OJjnahx;QRQMKycIYdVOz&D*E56AHHO`TTYJM zy?EnN*W}dATccBQYE%XMjp|j+Gr z2jv$exM27Wu893>hVh_&hQYro7_FqD;Wu!>&n@7BJj^(dK-)Dh0_fy9X62@>9en2%R5C%bL<8;Lf)=D89dg8@~FtTAkm zkQ}g3!R%jj8HH#O%P!ju$Su^TkW}ZLrq26t;(P*WKQ7a)gLalVh)En5lPGH68n~pS_g9k z$th1mqlc%8mFH4u9x=dzOBBqE^fua$@5n$)*l>$Q?a3 zIN`LnJDNv_&kc@`_ZfYuEPqjyyI`$5?amI!r@qJXZNY_#JKqF3JT_XXc{aE%OAh&K zQ+ff3K0x})@{F#Mc^8(a^1iM%!+x-Xund=gdDobaAwD{<&jj;%aAc0C)A(CS;uxW&M9Dv>0GW;D}T!^XQ$20Qh5*6kOvJG>*A5Oj*?5~7B2hNz`lgy=p zS`FU<`>O@Py${>^~&vdKL%3Glo&OYIhC)LA7EqX2L z2%E!=LpZDb9~ap{`71bWw^4UjS99N9ZCaX9Yh3mR{Bq3>`|4~?K{O}V?G(m$i{lm* z$$P~e)|QZ;i#tTVjD!yR!kOgK4aJ2m7ob)7^|y{(55;}W`XVqIjimMdhXugo|BKO+iZnkx26p*U0Tc-xSESR+CzI$=tr z1p%;A+AgWP^K?R2bxZ#Xr-}*rgBS|AZ2^fz$pw$2PQp!KY~J`*+9nrNRAI5si*ZYF z<*p1-7Nh^hxO!xvUx)lTP`C%^7mny z8ud$|V0TKt0zI-H(@Uyw!S31!NL@T|Y*2NuzERQHJ&ej7zRCvJ@p4Mr>tRj2;O&mK ziI#Cvq6P6F6a1;SIne_Z6c(X2@dXpdw#Qj5_jRcJ&7085yY*L$*8;Ekik>lhtg*t) z^H(k;$Hqsm=Etq^JP!q$__Hn7+KC?jVq}R-utqgLqRMy1`+}V%tdRB<(kces$0LFh z+=A8<4~Q(qN5S!FqNL)>;NU}|jEjBXgNh%C_#2{&F7|;(6u;AOuw8G7kW&_fKz{J7 zVgr}SsuJNN<2S(mFW9@{NwE2Y=%A})exMl>Wi2-X!ZdT_Dof1_h1RI$Lb zh7J{n>cGgBQJe(_k?oj^{ooOYVt%NnPz+&{o z#Rhm@F?x}lNHv==gc(I`6p=Epra9QX-Ef>&RaxPe!2lU!rX4?PYyc&TJ&74 N6kUm}>WX8^{{x`3#I67U diff --git a/ebpf/throughput/main.go b/ebpf/throughput/main.go index 793eeb4..01d7d92 100644 --- a/ebpf/throughput/main.go +++ b/ebpf/throughput/main.go @@ -24,6 +24,7 @@ type ThroughputEventBpf struct { DPort uint16 SAddr [16]byte DAddr [16]byte + IsIngress uint8 } // for user space @@ -34,6 +35,7 @@ type ThroughputEvent struct { DPort uint16 SAddr string DAddr string + IsIngress bool } // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. @@ -97,6 +99,7 @@ func DeployAndWait(ctx context.Context, ch chan interface{}, eventChan <-chan in DPort: bpfEvent.DPort, SAddr: fmt.Sprintf("%d.%d.%d.%d", bpfEvent.SAddr[0], bpfEvent.SAddr[1], bpfEvent.SAddr[2], bpfEvent.SAddr[3]), DAddr: fmt.Sprintf("%d.%d.%d.%d", bpfEvent.DAddr[0], bpfEvent.DAddr[1], bpfEvent.DAddr[2], bpfEvent.DAddr[3]), + IsIngress: bpfEvent.IsIngress != 0, } } @@ -148,11 +151,8 @@ func setFiltersOnCiliumInterfaces(objs bpfObjects) error { errs = append(errs, fmt.Errorf("failed to set up egress filter for link %s: %w", link.Attrs().Name, err)) } - // We were previously using an ingress filter in addition to the egress filter, but it wasn't actually doing anything. - // For now, we will just delete those until we can figure out how to make them work. - // Egress on its own is enough to track throughput between all pods in the cluster. - if err := deleteIngressFilters(link); err != nil { - errs = append(errs, fmt.Errorf("failed to set up ingress filter for link %s: %w", link.Attrs().Name, err)) + if err := setUpIngressFilter(link, objs); err != nil { + errs = append(errs, err) } } } @@ -190,6 +190,40 @@ func setUpEgressFilter(link netlink.Link, objs bpfObjects) error { return netlink.FilterReplace(filter) } +func setUpIngressFilter(link netlink.Link, objs bpfObjects) error { + existingFilters, err := netlink.FilterList(link, netlink.HANDLE_MIN_INGRESS) + if err != nil { + return err + } + + for _, filter := range existingFilters { + if bpfFilter, ok := filter.(*netlink.BpfFilter); ok { + if bpfFilter.Name == "throughput_bpf_ingress" || bpfFilter.Name == "packet_classifier" { + if err := netlink.FilterDel(filter); err != nil { + return err + } + } + } + } + + filter := &netlink.BpfFilter{ + FilterAttrs: netlink.FilterAttrs{ + LinkIndex: link.Attrs().Index, + Parent: netlink.HANDLE_MIN_INGRESS, + Protocol: unix.ETH_P_ALL, + Priority: 1, + }, + Fd: objs.bpfPrograms.PacketClassifier.FD(), + Name: "throughput_bpf_ingress", + DirectAction: true, + } + + if err := netlink.FilterReplace(filter); err != nil { + return fmt.Errorf("failed to set up ingress filter for link %s: %w", link.Attrs().Name, err) + } + return nil +} + func deleteIngressFilters(link netlink.Link) error { existingFilters, err := netlink.FilterList(link, netlink.HANDLE_MIN_INGRESS) if err != nil { @@ -209,3 +243,23 @@ func deleteIngressFilters(link netlink.Link) error { return nil } + +func deleteEgressFilters(link netlink.Link) error { + existingFilters, err := netlink.FilterList(link, netlink.HANDLE_MIN_EGRESS) + if err != nil { + return err + } + + for _, filter := range existingFilters { + if filter.Type() == "bpf" { + bpfFilter := filter.(*netlink.BpfFilter) + if bpfFilter.Name == "throughput_bpf_egress" { + if err := netlink.FilterDel(bpfFilter); err != nil { + return err + } + } + } + } + + return nil +} diff --git a/ebpf/throughput/throughput.c b/ebpf/throughput/throughput.c index f53a2dc..bd594b1 100644 --- a/ebpf/throughput/throughput.c +++ b/ebpf/throughput/throughput.c @@ -23,6 +23,7 @@ struct throughput_event __u16 dport; __u8 saddr[16]; __u8 daddr[16]; + __u8 is_ingress; }; // used for sending throughput events to userspace @@ -36,6 +37,11 @@ int packet_classifier(struct __sk_buff *skb) { void *data = (void*)(long)skb->data; void *data_end = (void*)(long)skb->data_end; + __u8 is_ingress = 0; + if (skb->ifindex == skb->ingress_ifindex) { + is_ingress = 1; + } + if (data + sizeof(struct ethhdr) > data_end) { return TC_ACT_UNSPEC; } @@ -58,6 +64,7 @@ int packet_classifier(struct __sk_buff *skb) { e->size = skb->len; e->sport = 0; e->dport = 0; + e->is_ingress = is_ingress; __builtin_memcpy(&e->saddr, &ip->saddr, sizeof(ip->saddr)); __builtin_memcpy(&e->daddr, &ip->daddr, sizeof(ip->daddr)); @@ -84,5 +91,5 @@ int packet_classifier(struct __sk_buff *skb) { bpf_ringbuf_submit(e, 0); } - return TC_ACT_OK; + return TC_ACT_UNSPEC; } diff --git a/go.mod b/go.mod index aac7f9e..a791723 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/cilium/ebpf v0.10.1-0.20230626090016-654491c8a500 github.com/prometheus/client_golang v1.16.0 github.com/rs/zerolog v1.29.1 - github.com/vishvananda/netlink v1.2.1-beta.2.0.20230807190133-6afddb37c1f0 + github.com/vishvananda/netlink v1.2.1-beta.2.0.20231206185938-4287122432b2 inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a k8s.io/api v0.27.2 k8s.io/apimachinery v0.27.2 @@ -67,7 +67,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/klog/v2 v2.100.1 k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/utils v0.0.0-20230308161112-d77c459e9343 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/go.sum b/go.sum index 669e294..6a4606c 100644 --- a/go.sum +++ b/go.sum @@ -152,8 +152,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/vishvananda/netlink v1.2.1-beta.2.0.20230807190133-6afddb37c1f0 h1:CLsXiDYQjYqJVntHkQZL2AW0R8BrvJu1K/hbs+2Q+EQ= -github.com/vishvananda/netlink v1.2.1-beta.2.0.20230807190133-6afddb37c1f0/go.mod h1:whJevzBpTrid75eZy99s3DqCmy05NfibNaF2Ol5Ox5A= +github.com/vishvananda/netlink v1.2.1-beta.2.0.20231206185938-4287122432b2 h1:iM+cUQFHKhXtqqDhorIvfYx/kLVHmPr8tWi2WPOHCuA= +github.com/vishvananda/netlink v1.2.1-beta.2.0.20231206185938-4287122432b2/go.mod h1:whJevzBpTrid75eZy99s3DqCmy05NfibNaF2Ol5Ox5A= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=