Skip to content

Commit

Permalink
Merge pull request #19 from andrewsykim/fix-address-family
Browse files Browse the repository at this point in the history
Update IP parsing based on netlink address attribute format
  • Loading branch information
andrewsykim authored Apr 28, 2020
2 parents 065f6ad + e63ad1c commit 4566cce
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 17 deletions.
4 changes: 3 additions & 1 deletion ipvs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ func checkDestination(t *testing.T, i *Handle, s *Service, d *Destination, check
assert.NilError(t, err)

for _, dst := range dstArray {
if dst.Address.Equal(d.Address) && dst.Port == d.Port && lookupFwMethod(dst.ConnectionFlags) == lookupFwMethod(d.ConnectionFlags) {
if dst.Address.Equal(d.Address) && dst.Port == d.Port &&
lookupFwMethod(dst.ConnectionFlags) == lookupFwMethod(d.ConnectionFlags) &&
dst.AddressFamily == d.AddressFamily {
dstFound = true
break
}
Expand Down
55 changes: 39 additions & 16 deletions netlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package ipvs
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
"os/exec"
Expand Down Expand Up @@ -351,17 +352,6 @@ func assembleService(attrs []syscall.NetlinkRouteAttr) (*Service, error) {

}

// in older kernels (< 3.18), the svc address family attribute may not exist so we must
// assume it based on the svc address provided.
if s.AddressFamily == 0 {
addr := (net.IP)(addressBytes)
if addr.To4() != nil {
s.AddressFamily = syscall.AF_INET
} else {
s.AddressFamily = syscall.AF_INET6
}
}

// parse Address after parse AddressFamily incase of parseIP error
if addressBytes != nil {
ip, err := parseIP(addressBytes, s.AddressFamily)
Expand Down Expand Up @@ -472,12 +462,14 @@ func assembleDestination(attrs []syscall.NetlinkRouteAttr) (*Destination, error)
// in older kernels (< 3.18), the destination address family attribute doesn't exist so we must
// assume it based on the destination address provided.
if d.AddressFamily == 0 {
addr := (net.IP)(addressBytes)
if addr.To4() != nil {
d.AddressFamily = syscall.AF_INET
} else {
d.AddressFamily = syscall.AF_INET6
// we can't check the address family using net stdlib because netlink returns
// IPv4 addresses as the first 4 bytes in a []byte of length 16 where as
// stdlib expects it as the last 4 bytes.
addressFamily, err := getIPFamily(addressBytes)
if err != nil {
return nil, err
}
d.AddressFamily = addressFamily
}

// parse Address after parse AddressFamily incase of parseIP error
Expand All @@ -492,6 +484,37 @@ func assembleDestination(attrs []syscall.NetlinkRouteAttr) (*Destination, error)
return &d, nil
}

// getIPFamily parses the IP family based on raw data from netlink.
// For AF_INET, netlink will set the first 4 bytes with trailing zeros
// 10.0.0.1 -> [10 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0]
// For AF_INET6, the full 16 byte array is used:
// 2001:db8:3c4d:15::1a00 -> [32 1 13 184 60 77 0 21 0 0 0 0 0 0 26 0]
func getIPFamily(address []byte) (uint16, error) {
if len(address) == 4 {
return syscall.AF_INET, nil
}

if isZeros(address) {
return 0, errors.New("could not parse IP family from address data")
}

// assume IPv4 if first 4 bytes are non-zero but rest of the data is trailing zeros
if !isZeros(address[:4]) && isZeros(address[4:]) {
return syscall.AF_INET, nil
}

return syscall.AF_INET6, nil
}

func isZeros(b []byte) bool {
for i := 0; i < len(b); i++ {
if b[i] != 0 {
return false
}
}
return true
}

// parseDestination given a ipvs netlink response this function will respond with a valid destination entry, an error otherwise
func (i *Handle) parseDestination(msg []byte) (*Destination, error) {
var dst *Destination
Expand Down
55 changes: 55 additions & 0 deletions netlink_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// +build linux

package ipvs

import (
"errors"
"reflect"
"syscall"
"testing"
)

func Test_getIPFamily(t *testing.T) {
testcases := []struct {
name string
address []byte
expectedFamily uint16
expectedErr error
}{
{
name: "16 byte IPv4 10.0.0.1",
address: []byte{10, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
expectedFamily: syscall.AF_INET,
expectedErr: nil,
},
{
name: "16 byte IPv6 2001:db8:3c4d:15::1a00",
address: []byte{32, 1, 13, 184, 60, 77, 0, 21, 0, 0, 0, 0, 0, 0, 26, 0},
expectedFamily: syscall.AF_INET6,
expectedErr: nil,
},
{
name: "zero address",
address: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
expectedFamily: 0,
expectedErr: errors.New("could not parse IP family from address data"),
},
}

for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
family, err := getIPFamily(testcase.address)
if !reflect.DeepEqual(err, testcase.expectedErr) {
t.Logf("got err: %v", err)
t.Logf("expected err: %v", testcase.expectedErr)
t.Errorf("unexpected error")
}

if family != testcase.expectedFamily {
t.Logf("got IP family: %v", family)
t.Logf("expected IP family: %v", testcase.expectedFamily)
t.Errorf("unexpected IP family")
}
})
}
}

0 comments on commit 4566cce

Please sign in to comment.