Skip to content
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

Destination address is not right #13

Closed
Chainyinghao opened this issue Nov 2, 2023 · 8 comments
Closed

Destination address is not right #13

Chainyinghao opened this issue Nov 2, 2023 · 8 comments

Comments

@Chainyinghao
Copy link

My destination address is ipv4,but when I use destination.Address(),it got a ipv6 address.Why?What happened?
image

image

@Chainyinghao
Copy link
Author

Chainyinghao commented Nov 2, 2023

When I use destination.Address.As16(),I got it,so amazing:
image

@terinjokes
Copy link
Contributor

Do you have a reproduction test case? I'm unable to reproduce this on my systems.

example.go
package main

import (
	"fmt"
	"log"

	"github.com/cloudflare/ipvs"
)

func main() {
	c, err := ipvs.New()
	if err != nil {
		log.Fatalf("error updating service: %v", err)
	}

	services, err := c.Services()
	if err != nil {
		log.Fatalf("error fetching services: %v", err)
	}

	for _, svc := range services {
		fmt.Printf("| %s:%d/%s %s\n", svc.Address, svc.Port, svc.Protocol, svc.Scheduler)

		destinations, err := c.Destinations(svc.Service)
		if err != nil {
			log.Fatalf("error fetching destinations: %v", err)
		}

		for _, dest := range destinations {
			fmt.Printf("|- %s:%d (%s) %d\n", dest.Address, dest.Port, dest.FwdMethod, dest.Weight)
		}
	}
}
ipvsadm -Ln
$ sudo ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.40.200:8080 wlc persistent 50
  -> 192.168.1.1:8080             Route   1      0          0
  -> 192.168.40.152:8080          Route   1      0          0
ipvsadm -Sn
$ sudo ipvsadm -Sn
-A -t 192.168.40.200:8080 -s wlc -p 50
-a -t 192.168.40.200:8080 -r 192.168.1.1:8080 -g -w 1
-a -t 192.168.40.200:8080 -r 192.168.40.152:8080 -g -w 1
example
$ sudo ./example
| 192.168.40.200:8080/TCP wlc
|- 192.168.1.1:8080 (DirectRoute) 1
|- 192.168.40.152:8080 (DirectRoute) 1

@Chainyinghao
Copy link
Author

This is the unit test I do:

  • lvs_new_test.go
func TestNewLVS(t *testing.T) {
	handle, _ := ipvs.New()
	services, _ := handle.Services()
	for _, svc := range services {
		fmt.Println("Address :", svc.Address)
		fmt.Println("Protocol:", svc.Protocol)
		fmt.Println("Port:", svc.Port)
		fmt.Println("Timeout:", svc.Timeout)
		fmt.Println("Scheduler:", svc.Scheduler)
		fmt.Println("Flags:", svc.Flags)
		fmt.Println("Stats:", svc.Stats)
		destinations, _ := handle.Destinations(svc.Service)
		if len(destinations) != 0 {
			for _, destination := range destinations {
				//ipv4Bytes := destination.Address.As16()
				//fmt.Println(destination.Address.StringExpanded())
				//ipv4 := net.IPv4(ipv4Bytes[0], ipv4Bytes[1], ipv4Bytes[2], ipv4Bytes[3])
				//fmt.Println("Address", ipv4.String())
				fmt.Println("Address", destination.Address)
				fmt.Printf("|- %s:%d (%s) %d\n", destination.Address, destination.Port, destination.FwdMethod, destination.Weight)
				fmt.Println("Port", destination.Port)
				fmt.Println("Weight", destination.Weight)
				fmt.Println("FwdMethod", destination.FwdMethod)
				fmt.Println("Family", destination.Family)
				fmt.Println("PersistentConnections", destination.PersistentConnections)
			}
		}
		if svc.Address.String() == "192.168.40.200" && svc.Port == 18443 {
			err := handle.RemoveService(svc.Service)
			if err != nil {
				t.Log(err)
				return
			}
		}
	}
}

image

@Chainyinghao
Copy link
Author

  • go version go1.21.3 linux/amd64

@Chainyinghao
Copy link
Author

I don't know what it happened, so I do this:

ipv4Bytes := destination.Address.As16()
ipv4 := net.IPv4(ipv4Bytes[0], ipv4Bytes[1], ipv4Bytes[2], ipv4Bytes[3])
fmt.Println("Address", ipv4.String())

@terinjokes
Copy link
Contributor

Thanks. That's an old kernel, and I wonder if the representation format changed. I'll try to track down CentOS 7.5 and see.

@terinjokes
Copy link
Contributor

terinjokes commented Nov 7, 2023

I can reproduce on "CentOS Linux release 7.9.2009" and kernel "3.10.0-1160.102.1.el7.x86_64". This extremely legacy kernel predates torvalds/linux@6cff339, which allows IPv4 traffic to be tunneled to an IPv6 destination by adding a per-destination address family. This patch was also not backported to the kernel CentOS uses.

[terin@localhost ~]$ grep IPVS_DEST_ATTR_ADDR_FAMILY /usr/src/kernels/3.10.0-1160.102.1.el7.x86_64/include/uapi/linux/ip_vs.h | wc -l
0

Note that this results in broken behavior if you attempt to configure an IPv6 tunnel destination for a IPv4 service, which neither the shipped version of ipvsadm or the latest upstream will prevent.

[terin@localhost ~]$ sudo ipvsadm -a -t 192.168.40.200:8080 -r '[2001:db8::1]:8080' -i
[terin@localhost ~]$ sudo ipvsadm -Sn
-A -t 192.168.40.200:8080 -s wlc -p 50
-a -t 192.168.40.200:8080 -r 32.1.13.184:8080 -i -w 1

This kernel is outside of the kernel releases we'd test against, predating even the oldest LTS release. The fix here is small, so I'll go ahead and include it, but you may run into other issues with other functionality.

terinjokes added a commit that referenced this issue Nov 7, 2023
In some legacy kernels IPVS did not support destinations with differing
address families from their services. This was changed to allow
tunneling IPv4 traffic over an IPv6 network, and in the process
destinations gained an address family field.

If this package was used on kernels predating this change, no address
family would have been found, and IP address parsing was done in IPv6
mode. This changeset now defaults to the service's address family if not
set by the destination.

Bug: #13
Reference: https://www.spinics.net/lists/lvs-devel/msg03723.html
@Chainyinghao
Copy link
Author

Thanks so much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants