Skip to content

Commit

Permalink
Create a veth interface for the bridge vlan config.
Browse files Browse the repository at this point in the history
  • Loading branch information
SchSeba committed Feb 3, 2019
1 parent 7dcdbc4 commit 0502f71
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 19 deletions.
101 changes: 90 additions & 11 deletions plugins/main/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,48 @@ func ensureBridgeAddr(br *netlink.Bridge, family int, ipn *net.IPNet, forceAddre
return nil
}

func ensureVlanInterfaceAddr(br *netlink.Bridge, vlan, family int, ipn *net.IPNet, forceAddress bool) error {
vlanIface, err := ensureVlanInterface(br, vlan)
if err != nil {
return err
}

addrs, err := netlink.AddrList(vlanIface, family)
if err != nil && err != syscall.ENOENT {
return fmt.Errorf("could not get list of IP addresses: %v", err)
}

ipnStr := ipn.String()
for _, a := range addrs {

// string comp is actually easiest for doing IPNet comps
if a.IPNet.String() == ipnStr {
return nil
}

// Multiple IPv6 addresses are allowed on the bridge if the
// corresponding subnets do not overlap. For IPv4 or for
// overlapping IPv6 subnets, reconfigure the IP address if
// forceAddress is true, otherwise throw an error.
if family == netlink.FAMILY_V4 || a.IPNet.Contains(ipn.IP) || ipn.Contains(a.IPNet.IP) {
if forceAddress {
if err = deleteVlanAddr(vlanIface, a.IPNet); err != nil {
return err
}
} else {
return fmt.Errorf("%q already has an IP address different from %v", br.Name, ipnStr)
}
}
}

addr := &netlink.Addr{IPNet: ipn, Label: ""}
if err := netlink.AddrAdd(vlanIface, addr); err != nil {
return fmt.Errorf("could not add IP address to %q: %v", vlanIface.Attrs().Name, err)
}

return nil
}

func deleteBridgeAddr(br *netlink.Bridge, ipn *net.IPNet) error {
addr := &netlink.Addr{IPNet: ipn, Label: ""}

Expand All @@ -198,6 +240,16 @@ func deleteBridgeAddr(br *netlink.Bridge, ipn *net.IPNet) error {
return nil
}

func deleteVlanAddr(vlan netlink.Link, ipn *net.IPNet) error {
addr := &netlink.Addr{IPNet: ipn, Label: ""}

if err := netlink.AddrDel(vlan, addr); err != nil {
return fmt.Errorf("could not remove IP address from %q: %v", vlan.Attrs().Name, err)
}

return nil
}

func bridgeByName(name string) (*netlink.Bridge, error) {
l, err := netlink.LinkByName(name)
if err != nil {
Expand Down Expand Up @@ -249,6 +301,34 @@ func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*net
return br, nil
}

func ensureVlanInterface(br *netlink.Bridge, vlanId int) (netlink.Link, error) {
name := fmt.Sprintf("%s.%d", br.Name, vlanId)

brGatewayVeth, err := netlink.LinkByName(name)
if err != nil {
if err.Error() != "Link not found" {
return nil, fmt.Errorf("failed to find interface %q: %v", name, err)
}

hostNS, err := ns.GetCurrentNS()
if err != nil {
return nil, fmt.Errorf("faild to find host namespace: %v", err)
}

_, brGatewayIface, err := setupVeth(hostNS, br, name, br.MTU, false, vlanId)
if err != nil {
return nil, fmt.Errorf("faild to create vlan gateway %q: %v", name, err)
}

brGatewayVeth, err = netlink.LinkByName(brGatewayIface.Name)
if err != nil {
return nil, fmt.Errorf("failed to lookup %q: %v", brGatewayIface.Name, err)
}
}

return brGatewayVeth, nil
}

func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool, vlanID int) (*current.Interface, *current.Interface, error) {
contIface := &current.Interface{}
hostIface := &current.Interface{}
Expand Down Expand Up @@ -312,13 +392,6 @@ func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {
return nil, nil, fmt.Errorf("failed to create bridge %q: %v", n.BrName, err)
}

if n.Vlan != 0 {
err = netlink.BridgeVlanAdd(br, uint16(n.Vlan), false, true, true, false)
if err != nil {
return nil, nil, fmt.Errorf("failed to setup vlan tag on the bridge interface %q: %v", br.Name, err)
}
}

return br, &current.Interface{
Name: br.Attrs().Name,
Mac: br.Attrs().HardwareAddr.String(),
Expand Down Expand Up @@ -461,10 +534,16 @@ func cmdAdd(args *skel.CmdArgs) error {
if gw.IP.To4() != nil && firstV4Addr == nil {
firstV4Addr = gw.IP
}

err = ensureBridgeAddr(br, gws.family, &gw, n.ForceAddress)
if err != nil {
return fmt.Errorf("failed to set bridge addr: %v", err)
if n.Vlan != 0 {
err = ensureVlanInterfaceAddr(br, n.Vlan, gws.family, &gw, n.ForceAddress)
if err != nil {
return fmt.Errorf("failed to set vlan interface for bridge with addr: %v", err)
}
} else {
err = ensureBridgeAddr(br, gws.family, &gw, n.ForceAddress)
if err != nil {
return fmt.Errorf("failed to set bridge addr: %v", err)
}
}
}

Expand Down
61 changes: 53 additions & 8 deletions plugins/main/bridge/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,26 @@ func delBridgeAddrs(testNS ns.NetNS) {
Expect(err).NotTo(HaveOccurred())
}

func delVlanAddrs(testNS ns.NetNS, vlan int) {
err := testNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()

vlanLink, err := netlink.LinkByName(fmt.Sprintf("%s.%d", BRNAME, vlan))
Expect(err).NotTo(HaveOccurred())
addrs, err := netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
Expect(err).NotTo(HaveOccurred())
for _, addr := range addrs {
if !addr.IP.IsLinkLocalUnicast() {
err = netlink.AddrDel(vlanLink, &addr)
Expect(err).NotTo(HaveOccurred())
}
}

return nil
})
Expect(err).NotTo(HaveOccurred())
}

func ipVersion(ip net.IP) string {
if ip.To4() != nil {
return "4"
Expand Down Expand Up @@ -331,21 +351,38 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) {
Expect(link.Attrs().HardwareAddr.String()).To(Equal(result.Interfaces[0].Mac))
bridgeMAC := link.Attrs().HardwareAddr.String()

// Check the bridge vlan filtering equals true
if tc.vlan != 0 {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
var vlanLink netlink.Link
if !tc.isLayer2 && tc.vlan != 0 {
// Make sure vlan link exists
vlanLink, err = netlink.LinkByName(fmt.Sprintf("%s.%d", BRNAME, tc.vlan))
Expect(err).NotTo(HaveOccurred())
Expect(vlanLink.Attrs().Name).To(Equal(fmt.Sprintf("%s.%d", BRNAME, tc.vlan)))
Expect(vlanLink).To(BeAssignableToTypeOf(&netlink.Veth{}))

// Check the bridge dot vlan interface have the vlan tag
peerLink, err := netlink.LinkByIndex(vlanLink.Attrs().Index - 1)
Expect(err).NotTo(HaveOccurred())
interfaceMap, err := netlink.BridgeVlanList()
Expect(err).NotTo(HaveOccurred())
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
Expect(isExist).To(BeTrue())
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
}

// Check the bridge vlan filtering equals true
if tc.vlan != 0 {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
} else {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false))
}

// Ensure bridge has expected gateway address(es)
addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL)
var addrs []netlink.Addr
if tc.vlan == 0 {
addrs, err = netlink.AddrList(link, netlink.FAMILY_ALL)
} else {
addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
}
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(BeNumerically(">", 0))
for _, cidr := range tc.expGWCIDRs {
Expand All @@ -367,7 +404,11 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) {
// Check for the veth link in the main namespace
links, err := netlink.LinkList()
Expect(err).NotTo(HaveOccurred())
Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback
if !tc.isLayer2 && tc.vlan != 0 {
Expect(len(links)).To(Equal(5)) // Bridge, Bridge vlan veth, veth, and loopback
} else {
Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback
}

link, err = netlink.LinkByName(result.Interfaces[1].Name)
Expect(err).NotTo(HaveOccurred())
Expand All @@ -387,7 +428,7 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) {
// If not, it means the bridge has an unstable mac and will change
// as ifs are added and removed
// this check is not relevant for a layer 2 bridge
if !tc.isLayer2 {
if !tc.isLayer2 && tc.vlan == 0 {
Expect(link.Attrs().HardwareAddr.String()).NotTo(Equal(bridgeMAC))
}

Expand Down Expand Up @@ -634,6 +675,10 @@ func cmdAddDelTest(testNS ns.NetNS, tc testCase, dataDir string) {

// Clean up bridge addresses for next test case
delBridgeAddrs(testNS)

if tc.vlan != 0 && !tc.isLayer2 {
delVlanAddrs(testNS, tc.vlan)
}
}

var _ = Describe("bridge Operations", func() {
Expand Down Expand Up @@ -773,7 +818,7 @@ var _ = Describe("bridge Operations", func() {
cmdAddDelTest(originalNS, tc, dataDir)
})

It("configures and deconfigures a bridge and veth with default route and vlanID 100 with ADD/DEL for 0.3.0 config", func() {
It("configures and deconfigures a bridge, veth with default route and vlanID 100 with ADD/DEL for 0.3.0 config", func() {
testCases := []testCase{
{
// IPv4 only
Expand Down

0 comments on commit 0502f71

Please sign in to comment.