First version CNI plugin
This commit is contained in:
parent
f71a82f785
commit
9b7dbf733d
10
10-mycelium.conflist
Normal file
10
10-mycelium.conflist
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"cniVersion": "1.0.0",
|
||||||
|
"name": "mycelium-network",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "mycelium-cni",
|
||||||
|
"myceliumInterface": "mycelium"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
26
Makefile
Normal file
26
Makefile
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
PLUGIN_NAME = mycelium-cni
|
||||||
|
CNI_PLUGINS_DIR = /opt/cni/bin
|
||||||
|
CNI_CONFIG_DIR = /etc/cni/net.d
|
||||||
|
|
||||||
|
.PHONY: build install clean test
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build -o $(PLUGIN_NAME) .
|
||||||
|
|
||||||
|
install: build
|
||||||
|
sudo cp $(PLUGIN_NAME) $(CNI_PLUGINS_DIR)/
|
||||||
|
sudo cp 10-mycelium.conflist $(CNI_CONFIG_DIR)/
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(PLUGIN_NAME)
|
||||||
|
sudo rm -f $(CNI_PLUGINS_DIR)/$(PLUGIN_NAME)
|
||||||
|
sudo rm -f $(CNI_CONFIG_DIR)/10-mycelium.conflist
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
go fmt ./...
|
||||||
|
|
||||||
|
vet:
|
||||||
|
go vet ./...
|
63
README.md
Normal file
63
README.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Mycelium CNI Plugin
|
||||||
|
|
||||||
|
A Container Network Interface (CNI) plugin that enables Kubernetes containers to connect to the Mycelium network.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This CNI plugin integrates with the Mycelium overlay network to provide IPv6 connectivity for Kubernetes containers. It creates veth pairs and assigns IPv6 addresses from the host's Mycelium /64 block to containers.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Mycelium daemon running on the host
|
||||||
|
- Go 1.21+
|
||||||
|
- Root privileges for installation
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the plugin
|
||||||
|
make build
|
||||||
|
|
||||||
|
# Install plugin and configuration
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The plugin uses a CNI configuration file (`10-mycelium.conflist`) that specifies the Mycelium interface name:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cniVersion": "1.0.0",
|
||||||
|
"name": "mycelium-network",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "mycelium-cni",
|
||||||
|
"myceliumInterface": "mycelium"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## How it Works
|
||||||
|
|
||||||
|
1. **ADD Operation**: Creates a veth pair, moves one end to the container namespace, assigns an IPv6 address from the Mycelium prefix, and sets up routing.
|
||||||
|
|
||||||
|
2. **DEL Operation**: Cleans up the host-side veth interface when containers are destroyed.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Start Mycelium daemon first:
|
||||||
|
```bash
|
||||||
|
sudo mycelium --peers tcp://188.40.132.242:9651 tcp://136.243.47.186:9651
|
||||||
|
```
|
||||||
|
|
||||||
|
Then use with Kubernetes or test directly with CNI tools.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Based on the docker-demo.sh script, this plugin:
|
||||||
|
- Uses IPv6 addressing from Mycelium's /64 block
|
||||||
|
- Creates veth pairs for container connectivity
|
||||||
|
- Sets up routing for Mycelium network (400::/7)
|
||||||
|
- Enables IPv6 forwarding on the host
|
9
go.mod
Normal file
9
go.mod
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module mycelium-cni
|
||||||
|
|
||||||
|
go 1.21
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/containernetworking/cni v1.1.2
|
||||||
|
github.com/vishvananda/netlink v1.1.0
|
||||||
|
github.com/vishvananda/netns v0.0.4
|
||||||
|
)
|
249
main.go
Normal file
249
main.go
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"github.com/vishvananda/netns"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PluginName = "mycelium-cni"
|
||||||
|
MyceliumInterface = "mycelium"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetConf struct {
|
||||||
|
types.NetConf
|
||||||
|
MyceliumInterface string `json:"myceliumInterface,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, "mycelium CNI plugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
|
conf, err := parseConfig(args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get container network namespace
|
||||||
|
containerNS, err := netns.GetFromPath(args.Netns)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open container netns %q: %v", args.Netns, err)
|
||||||
|
}
|
||||||
|
defer containerNS.Close()
|
||||||
|
|
||||||
|
// Get Mycelium interface and its IPv6 prefix
|
||||||
|
myceliumIP, err := getMyceliumIP(conf.MyceliumInterface)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get Mycelium IP: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create veth pair
|
||||||
|
hostVethName := fmt.Sprintf("veth-%s", args.ContainerID[:8])
|
||||||
|
containerVethName := "eth0"
|
||||||
|
|
||||||
|
hostVeth, containerVeth, err := createVethPair(hostVethName, containerVethName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create veth pair: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move container veth to container namespace
|
||||||
|
if err := netlink.LinkSetNsFd(containerVeth, int(containerNS)); err != nil {
|
||||||
|
return fmt.Errorf("failed to move veth to container netns: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure container interface
|
||||||
|
containerIP := generateContainerIP(myceliumIP, args.ContainerID)
|
||||||
|
if err := configureContainerInterface(containerNS, containerVethName, containerIP, hostVethName); err != nil {
|
||||||
|
return fmt.Errorf("failed to configure container interface: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure host interface and routing
|
||||||
|
if err := configureHostInterface(hostVeth, containerIP); err != nil {
|
||||||
|
return fmt.Errorf("failed to configure host interface: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build result
|
||||||
|
result := ¤t.Result{
|
||||||
|
CNIVersion: current.ImplementedSpecVersion,
|
||||||
|
Interfaces: []*current.Interface{
|
||||||
|
{
|
||||||
|
Name: hostVethName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: containerVethName,
|
||||||
|
Sandbox: args.Netns,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
IPs: []*current.IPConfig{
|
||||||
|
{
|
||||||
|
Interface: current.Int(1), // container interface
|
||||||
|
Address: net.IPNet{IP: containerIP, Mask: net.CIDRMask(64, 128)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.PrintResult(result, conf.CNIVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
|
// Basic connectivity check could be implemented here
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
|
// Clean up veth pair (host side will be automatically removed)
|
||||||
|
hostVethName := fmt.Sprintf("veth-%s", args.ContainerID[:8])
|
||||||
|
|
||||||
|
link, err := netlink.LinkByName(hostVethName)
|
||||||
|
if err != nil {
|
||||||
|
// Interface might already be gone, which is fine
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return netlink.LinkDel(link)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseConfig(stdin []byte) (*NetConf, error) {
|
||||||
|
conf := &NetConf{
|
||||||
|
MyceliumInterface: MyceliumInterface,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(stdin, conf); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse network configuration: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMyceliumIP(interfaceName string) (net.IP, error) {
|
||||||
|
link, err := netlink.LinkByName(interfaceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to find interface %s: %v", interfaceName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V6)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get addresses for %s: %v", interfaceName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if addr.IP.IsGlobalUnicast() {
|
||||||
|
// Return the /64 prefix
|
||||||
|
return addr.IP.Mask(net.CIDRMask(64, 128)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no global IPv6 address found on %s", interfaceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateContainerIP(myceliumPrefix net.IP, containerID string) net.IP {
|
||||||
|
// Generate a container IP within the /64 prefix
|
||||||
|
// Using simple approach: prefix + ::1 (could be made more sophisticated)
|
||||||
|
containerIP := make(net.IP, len(myceliumPrefix))
|
||||||
|
copy(containerIP, myceliumPrefix)
|
||||||
|
containerIP[15] = 1 // Set last byte to 1
|
||||||
|
return containerIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func createVethPair(hostName, containerName string) (netlink.Link, netlink.Link, error) {
|
||||||
|
veth := &netlink.Veth{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{Name: hostName},
|
||||||
|
PeerName: containerName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := netlink.LinkAdd(veth); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hostVeth, err := netlink.LinkByName(hostName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
containerVeth, err := netlink.LinkByName(containerName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bring up host side
|
||||||
|
if err := netlink.LinkSetUp(hostVeth); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostVeth, containerVeth, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureContainerInterface(containerNS netns.NsHandle, ifName string, containerIP net.IP, hostVethName string) error {
|
||||||
|
return netns.Set(containerNS, func(ns netns.NsHandle) error {
|
||||||
|
// Get the interface
|
||||||
|
link, err := netlink.LinkByName(ifName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bring interface up
|
||||||
|
if err := netlink.LinkSetUp(link); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add IP address
|
||||||
|
addr := &netlink.Addr{
|
||||||
|
IPNet: &net.IPNet{
|
||||||
|
IP: containerIP,
|
||||||
|
Mask: net.CIDRMask(64, 128),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := netlink.AddrAdd(link, addr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get host veth link-local address for routing
|
||||||
|
hostVeth, err := netlink.LinkByName(hostVethName)
|
||||||
|
if err == nil {
|
||||||
|
hostAddrs, err := netlink.AddrList(hostVeth, netlink.FAMILY_V6)
|
||||||
|
if err == nil {
|
||||||
|
for _, addr := range hostAddrs {
|
||||||
|
if addr.IP.IsLinkLocalUnicast() {
|
||||||
|
// Add route to Mycelium network via host veth
|
||||||
|
route := &netlink.Route{
|
||||||
|
Dst: &net.IPNet{
|
||||||
|
IP: net.ParseIP("400::"),
|
||||||
|
Mask: net.CIDRMask(7, 128),
|
||||||
|
},
|
||||||
|
Gw: addr.IP,
|
||||||
|
LinkIndex: link.Attrs().Index,
|
||||||
|
}
|
||||||
|
netlink.RouteAdd(route)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureHostInterface(hostVeth netlink.Link, containerIP net.IP) error {
|
||||||
|
// Add route to container IP via host veth
|
||||||
|
route := &netlink.Route{
|
||||||
|
Dst: &net.IPNet{
|
||||||
|
IP: containerIP,
|
||||||
|
Mask: net.CIDRMask(128, 128),
|
||||||
|
},
|
||||||
|
LinkIndex: hostVeth.Attrs().Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
return netlink.RouteAdd(route)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user