Skip to content

Commit

Permalink
Add windows vxlan
Browse files Browse the repository at this point in the history
    - patch for flannel-io#922
  • Loading branch information
thxCode committed Sep 3, 2018
1 parent 25d7ac2 commit 1a704c2
Show file tree
Hide file tree
Showing 4 changed files with 1,073 additions and 2 deletions.
79 changes: 79 additions & 0 deletions Vagrantfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.

# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
# config.vm.box = "ubuntu/xenial64"

# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false

# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# NOTE: This will enable public access to the opened port
# config.vm.network "forwarded_port", guest: 80, host: 8080

# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"

# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"

# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"

# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"

# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.

# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# apt-get update
# apt-get install -y apache2
# SHELL

config.vm.box = "maiwj/ubuntu-xenial64"
config.vm.synced_folder "..", "/go/src/github.com/coreos"
config.vm.provider "virtualbox" do |vb|
vb.name = "flanneld-package"
vb.cpus = 2
vb.memory = 1024
end

end
193 changes: 193 additions & 0 deletions backend/vxlan/vxlan_network_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Copyright 2015 flannel authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package vxlan

import (
"encoding/json"
"fmt"
"net"
"sync"
"time"

log "github.com/golang/glog"
"golang.org/x/net/context"

"github.com/coreos/flannel/backend"
"github.com/coreos/flannel/subnet"

"github.com/Microsoft/hcsshim"
"github.com/coreos/flannel/pkg/ip"
)

type network struct {
name string
networkId string
macPrefix string
extIface *backend.ExternalInterface
lease *subnet.Lease
sm subnet.Manager
}

func (n *network) Lease() *subnet.Lease {
return n.lease
}

func (n *network) MTU() int {
return n.extIface.Iface.MTU
}

func (n *network) Run(ctx context.Context) {
wg := sync.WaitGroup{}

log.Info("Watching for new subnet leases")
evts := make(chan []subnet.Event)
wg.Add(1)
go func() {
subnet.WatchLeases(ctx, n.sm, n.lease, evts)
wg.Done()
}()

defer wg.Wait()

for {
select {
case evtBatch := <-evts:
n.handleSubnetEvents(evtBatch)

case <-ctx.Done():
return
}
}
}

func conjureMac(macPrefix string, ip ip.IP4) string {
a, b, c, d := ip.Octets()
return fmt.Sprintf("%v-%02x-%02x-%02x-%02x", macPrefix, a, b, c, d)
}

func (n *network) handleSubnetEvents(batch []subnet.Event) {
for _, evt := range batch {
if evt.Lease.Attrs.BackendType != "vxlan" {
log.Warningf("Ignoring non-vxlan subnet: type=%v", evt.Lease.Attrs.BackendType)
continue
}

if evt.Type != subnet.EventAdded && evt.Type != subnet.EventRemoved {
log.Error("Internal error: unknown event type: ", int(evt.Type))
continue
}

// add or delete all possible remote IPs (excluding gateway & bcast) as remote endpoints
managementIp := evt.Lease.Attrs.PublicIP.String()
lastIP := evt.Lease.Subnet.Next().IP - 1

start := time.Now()
for remoteIp := evt.Lease.Subnet.IP + 2; remoteIp < lastIP; remoteIp++ {
remoteMac := conjureMac(n.macPrefix, remoteIp)
remoteEndpointName := fmt.Sprintf("remote_%v", remoteIp.String())

if evt.Type == subnet.EventAdded {
if err := createRemoteEndpoint(remoteEndpointName, remoteIp, remoteMac, managementIp, n.networkId); err != nil {
log.Errorf("failed to create remote endpoint [%v], error: %v", remoteEndpointName, err)
}
} else {
if hnsEndpoint, err := hcsshim.GetHNSEndpointByName(remoteEndpointName); err != nil {
if _, err := hnsEndpoint.Delete(); err != nil {
log.Errorf("failed to delete existing remote endpoint [%v], error: %v", remoteEndpointName, err)
}
}
}
}

t := time.Now()
elapsed := t.Sub(start)

message := "Subnet removed"
if evt.Type == subnet.EventAdded {
message = "Subnet added"
}
log.Infof("%v: %v [%v ns]", message, evt.Lease.Subnet, elapsed.Nanoseconds())
}
}

func checkPAAddress(hnsEndpoint *hcsshim.HNSEndpoint, managementAddress string) bool {
if hnsEndpoint.Policies == nil {
return false
}

for _, policyJson := range hnsEndpoint.Policies {
var policy map[string]interface{}
if json.Unmarshal(policyJson, &policy) != nil {
return false
}

if valType, ok := policy["Type"]; ok && valType.(string) == "PA" {
if val, ok := policy["PA"]; ok {
if val.(string) == managementAddress {
return true
}
}
}
}

return false
}

func createRemoteEndpoint(remoteEndpointName string, remoteIp ip.IP4, remoteMac string, managementAddress string, networkId string) error {

// find existing
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(remoteEndpointName)
if err == nil && hnsEndpoint.VirtualNetwork == networkId && checkPAAddress(hnsEndpoint, managementAddress) {
return nil
}

// create or replace endpoint
if hnsEndpoint != nil {
if _, err = hnsEndpoint.Delete(); err != nil {
log.Errorf("failed to delete existing remote endpoint [%v], error: %v", remoteEndpointName, err)
return err
}
}

paPolicy := struct {
Type string
PA string
}{
Type: "PA",
PA: managementAddress,
}

policyBytes, _ := json.Marshal(&paPolicy)

hnsEndpoint = &hcsshim.HNSEndpoint{
Id: "",
Name: remoteEndpointName,
IPAddress: net.IPv4(remoteIp.Octets()),
MacAddress: remoteMac,
VirtualNetwork: networkId,
IsRemoteEndpoint: true,
Policies: []json.RawMessage{
policyBytes,
},
}

hnsEndpoint, err = hnsEndpoint.Create()
if err != nil {
log.Errorf("failed to create remote endpoint [%v], error: %v", remoteEndpointName, err)
return err
}

return nil
}
Loading

0 comments on commit 1a704c2

Please sign in to comment.