Skip to content

Commit 0fdf3b9

Browse files
committed
Workaround for K8s dual stack bug
K8s doesn't have a functioning way to get the IPv6 Pod IP via the downward API. This hacks around it if env variable NSM_TUNNEL_IP_TO_V6 = true The first GlobalUnicast IPv6 address from the same kernel interface as the IPv4 Tunnel IP is used instead. Signed-off-by: Ed Warnicke <[email protected]>
1 parent a575fe0 commit 0fdf3b9

File tree

4 files changed

+48
-0
lines changed

4 files changed

+48
-0
lines changed

.golangci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ issues:
171171
linters:
172172
- funlen
173173
text: "Function 'main'"
174+
- path: main.go
175+
linters:
176+
- gocyclo
177+
text: "cyclomatic complexity 17 of func `main` is high"
174178
- path: internal/vppinit/vppinit.go
175179
linters:
176180
- funlen

internal/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type Config struct {
4545
OpenTelemetryEndpoint string `default:"otel-collector.observability.svc.cluster.local:4317" desc:"OpenTelemetry Collector Endpoint"`
4646

4747
TunnelIP net.IP `desc:"IP to use for tunnels" split_words:"true"`
48+
TunnelIPToV6 bool `desc:"If NSM_TUNNEL_IP set to IPv4 address, use IPv6 address from same interface" split_words:"true"`
4849
VxlanPort uint16 `default:"0" desc:"VXLAN port to use" split_words:"true"`
4950
VppAPISocket string `default:"/var/run/vpp/external/vpp-api.sock" desc:"filename of socket to connect to existing VPP instance. If empty a VPP instance is run in forwarder" split_words:"true"`
5051
VppInit vppinit.Func `default:"NONE" desc:"type of VPP initialization. Must be NONE or AF_PACKET" split_words:"true"`

internal/vppinit/vppinit.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,39 @@ func linkByIP(ctx context.Context, ipaddress net.IP) (netlink.Link, error) {
337337
}
338338
return nil, nil
339339
}
340+
341+
// TunnelIPtoIPv6 - Converts TunnelIP to IPv6
342+
//
343+
// In DualStack K8s, it is not currently possible to get the IPv6 PodIP
344+
// via the Downward API. For this reason, we need a mechanism to support
345+
// taking the IPv4 Pod address received via the Downward API and replacing
346+
// it with the IPv6 IP on the same interface. This function does so
347+
// by taking the first GlobalUnicast IPv6 address from the same interface and
348+
// returning it.
349+
func TunnelIPtoIPv6(ctx context.Context, tunnelIP net.IP) (net.IP, error) {
350+
if tunnelIP == nil || tunnelIP.To4() == nil {
351+
return tunnelIP, nil
352+
}
353+
354+
link, err := linkByIP(ctx, tunnelIP)
355+
if err != nil {
356+
return nil, err
357+
}
358+
now := time.Now()
359+
addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL)
360+
if err != nil {
361+
return nil, errors.Wrapf(err, "could not retrieve addresses for link %s", link.Attrs().Name)
362+
}
363+
364+
log.FromContext(ctx).
365+
WithField("duration", time.Since(now)).
366+
WithField("link", link.Attrs().Name).
367+
WithField("netlink", "AddrList").Debug("completed")
368+
369+
for _, addr := range addrs {
370+
if addr.IP != nil && addr.IP.To4() == nil && addr.IP.IsGlobalUnicast() {
371+
tunnelIP = addr.IP
372+
}
373+
}
374+
return tunnelIP, nil
375+
}

main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,13 @@ func main() {
190190
log.FromContext(ctx).Warn("SR-IOV is not enabled")
191191
}
192192

193+
if cfg.TunnelIP != nil && cfg.TunnelIP.To4() != nil && cfg.TunnelIPToV6 {
194+
cfg.TunnelIP, err = vppinit.TunnelIPtoIPv6(ctx, cfg.TunnelIP)
195+
if err != nil {
196+
logrus.Fatalf("error converting IPv4 TunnelIP to IPv6 TunnelIP: %+v", err)
197+
}
198+
}
199+
193200
deviceMap := setupDeviceMap(ctx, cfg)
194201
err = vppinit.InitLinks(ctx, vppConn, deviceMap, cfg.TunnelIP)
195202
if err != nil {

0 commit comments

Comments
 (0)