func (d NetworkDriver) Join(request *network.JoinRequest) (*network.JoinResponse, error) {
	logutils.JSONMessage("Join", request)

	// 1) Set up a veth pair
	// 	The one end will stay in the host network namespace - named caliXXXXX
	//	The other end is given a temporary name. It's moved into the final network namespace by libnetwork itself.
	var err error
	prefix := request.EndpointID[:mathutils.MinInt(11, len(request.EndpointID))]
	hostInterfaceName := "cali" + prefix
	tempInterfaceName := "temp" + prefix

	if err = netns.CreateVeth(hostInterfaceName, tempInterfaceName); err != nil {
		err = errors.Wrapf(
			err, "Veth creation error, hostInterfaceName=%v, tempInterfaceName=%v",
			hostInterfaceName, tempInterfaceName)
		return nil, err

	// libnetwork doesn't set the MAC address properly, so set it here.
	if err = netns.SetVethMac(tempInterfaceName, d.fixedMac); err != nil {
		log.Debugf("Veth mac setting for %v failed, removing veth for %v\n", tempInterfaceName, hostInterfaceName)
		err = netns.RemoveVeth(hostInterfaceName)
		err = errors.Wrapf(err, "Veth removing for %v error", hostInterfaceName)
		return nil, err

	resp := &network.JoinResponse{
		InterfaceName: network.InterfaceName{
			SrcName:   tempInterfaceName,
			DstPrefix: IFPrefix,

	// One of the network gateway addresses indicate that we are using
	// Calico IPAM driver.  In this case we setup routes using the gateways
	// configured on the endpoint (which will be our host IPs).
	log.Debugln("Using Calico IPAM driver, configure gateway and static routes to the host")

	resp.Gateway = d.DummyIPV4Nexthop
	resp.StaticRoutes = append(resp.StaticRoutes, &network.StaticRoute{
		Destination: d.DummyIPV4Nexthop + "/32",
		RouteType:   1, // 1 = CONNECTED
		NextHop:     "",

	logutils.JSONMessage("Join response", resp)

	return resp, nil
func (d NetworkDriver) CreateEndpoint(request *network.CreateEndpointRequest) (*network.CreateEndpointResponse, error) {
	logutils.JSONMessage("CreateEndpoint", request)

	hostname, err := osutils.GetHostname()
	if err != nil {
		err = errors.Wrap(err, "Hostname fetching error")
		return nil, err

	log.Debugf("Creating endpoint %v\n", request.EndpointID)
	if request.Interface.Address == "" {
		err := errors.New("No address assigned for endpoint")
		return nil, err

	var addresses []caliconet.IPNet
	if request.Interface.Address != "" {
		// Parse the address this function was passed. Ignore the subnet - Calico always uses /32 (for IPv4)
		ip4, _, err := net.ParseCIDR(request.Interface.Address)
		log.Debugf("Parsed IP %v from (%v) \n", ip4, request.Interface.Address)

		if err != nil {
			err = errors.Wrapf(err, "Parsing %v as CIDR failed", request.Interface.Address)
			return nil, err

		addresses = append(addresses, caliconet.IPNet{IPNet: net.IPNet{IP: ip4, Mask: net.CIDRMask(32, 32)}})

	endpoint := api.NewWorkloadEndpoint()
	endpoint.Metadata.Node = hostname
	endpoint.Metadata.Orchestrator = d.orchestratorID
	endpoint.Metadata.Workload = d.containerName
	endpoint.Metadata.Name = request.EndpointID
	endpoint.Spec.InterfaceName = "cali" + request.EndpointID[:mathutils.MinInt(11, len(request.EndpointID))]
	mac, _ := net.ParseMAC(d.fixedMac)
	endpoint.Spec.MAC = &caliconet.MAC{HardwareAddr: mac}
	endpoint.Spec.IPNetworks = append(endpoint.Spec.IPNetworks, addresses...)

	// Use the Docker API to fetch the network name (so we don't have to use an ID everywhere)
	dockerCli, err := dockerClient.NewEnvClient()
	if err != nil {
		err = errors.Wrap(err, "Error while attempting to instantiate docker client from env")
		return nil, err
	defer dockerCli.Close()
	networkData, err := dockerCli.NetworkInspect(context.Background(), request.NetworkID)
	if err != nil {
		err = errors.Wrapf(err, "Network %v inspection error", request.NetworkID)
		return nil, err

	// Now that we know the network name, set it on the endpoint.
	endpoint.Spec.Profiles = append(endpoint.Spec.Profiles, networkData.Name)

	// If a profile for the network name doesn't exist then it needs to be created.
	// We always attempt to create the profile and rely on the datastore to reject
	// the request if the profile already exists.
	profile := &api.Profile{
		Metadata: api.ProfileMetadata{
			Name: networkData.Name,
			Tags: []string{networkData.Name},
		Spec: api.ProfileSpec{
			EgressRules:  []api.Rule{{Action: "allow"}},
			IngressRules: []api.Rule{{Action: "allow", Source: api.EntityRule{Tag: networkData.Name}}},
	if _, err := d.client.Profiles().Create(profile); err != nil {
		if _, ok := err.(libcalicoErrors.ErrorResourceAlreadyExists); !ok {
			return nil, err

	// Create the endpoint last to minimize side-effects if something goes wrong.
	_, err = d.client.WorkloadEndpoints().Create(endpoint)
	if err != nil {
		err = errors.Wrapf(err, "Workload endpoints creation error, data: %+v", endpoint)
		return nil, err

	log.Debugf("Workload created, data: %+v\n", endpoint)

	response := &network.CreateEndpointResponse{
		Interface: &network.EndpointInterface{
			MacAddress: string(d.fixedMac),

	logutils.JSONMessage("CreateEndpoint response", response)

	return response, nil
func (d NetworkDriver) Leave(request *network.LeaveRequest) error {
	logutils.JSONMessage("Leave response", request)
	caliName := "cali" + request.EndpointID[:mathutils.MinInt(11, len(request.EndpointID))]
	err := netns.RemoveVeth(caliName)
	return err
			DockerString(fmt.Sprintf("docker network create %s -d calico --ipam-driver calico-ipam", name))
		AfterEach(func() {
			DockerString(fmt.Sprintf("docker network rm %s", name))

		It("creates a container on a network  and checks all assertions", func() {
			// Create a container that will just sit in the background
			DockerString(fmt.Sprintf("docker run --net %s -tid --name %s busybox", name, name))

			// Gather information for assertions
			docker_endpoint := GetDockerEndpoint(name, name)
			ip := docker_endpoint.IPAddress
			mac := docker_endpoint.MacAddress
			endpoint_id := docker_endpoint.EndpointID
			interface_name := "cali" + endpoint_id[:mathutils.MinInt(11, len(endpoint_id))]

			// Check that the endpoint is created in etcd
			etcd_endpoint := GetEtcdString(fmt.Sprintf("/calico/v1/host/test/workload/libnetwork/libnetwork/endpoint/%s", endpoint_id))
				interface_name, mac, name, ip)))

			// Check profile
			tags := GetEtcdString(fmt.Sprintf("/calico/v1/policy/profile/%s/tags", name))
			labels := GetEtcdString(fmt.Sprintf("/calico/v1/policy/profile/%s/labels", name))
			rules := GetEtcdString(fmt.Sprintf("/calico/v1/policy/profile/%s/rules", name))
			Expect(tags).Should(MatchJSON(fmt.Sprintf(`["%s"]`, name)))
			Expect(rules).Should(MatchJSON(fmt.Sprintf(`{"inbound_rules": [{"action": "allow","src_tag": "%s"}],"outbound_rules":[{"action": "allow"}]}`, name)))