go.ligato.io/vpp-agent/v3@v3.5.0/examples/localclient_linux/vrf/main.go (about)

     1  // Copyright (c) 2021 Cisco and/or its affiliates.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at:
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"context"
    19  	"log"
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/namsral/flag"
    24  	"go.ligato.io/cn-infra/v2/agent"
    25  	"go.ligato.io/cn-infra/v2/logging"
    26  	"go.ligato.io/cn-infra/v2/logging/logrus"
    27  
    28  	"go.ligato.io/vpp-agent/v3/clientv2/linux/localclient"
    29  	"go.ligato.io/vpp-agent/v3/cmd/vpp-agent/app"
    30  	linux_ifplugin "go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin"
    31  	linux_nsplugin "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin"
    32  	"go.ligato.io/vpp-agent/v3/plugins/orchestrator"
    33  	vpp_ifplugin "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin"
    34  	linux_intf "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
    35  	vpp_intf "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    36  )
    37  
    38  var (
    39  	timeout = flag.Int("timeout", 20, "Timeout between applying of initial and modified configuration in seconds")
    40  )
    41  
    42  /* Confgiuration */
    43  
    44  // Example configures two VRF devices. Then two TAP interfaces on the vpp with other ends in the default namespace.
    45  // Both Linux taps are set with IP address
    46  /**********************************************
    47   * Initial Data                               *
    48   *                                            *
    49   *  +--------------------------------------+  *
    50   *  |                                      |  *
    51   *  |   +-------+             +-------+    |  *
    52   *  |   | tap1  |             | tap2  |    |  *
    53   *  |   +---+---+             +---+---+    |  *
    54   *  |       |                     |        |  *
    55   *  +-------+------------------------------+  *
    56   *          |                     |           *
    57   *  +-------+---------+   +-------+--------+  *
    58   *  | linux-tap1      |   | linux-tap1     |  *
    59   *  | IP: 10.0.0.2/24 |   | IP: 20.0.0.2/24|  *
    60   *  +-----------------+   +----------------+  *
    61   *  VRF-dev-1			  VRF-dev-2           *
    62   *                                            *
    63   **********************************************/
    64  
    65  // Next step switches Linux TAP VRFs
    66  /**********************************************
    67   * Initial Data                               *
    68   *                                            *
    69   *  +--------------------------------------+  *
    70   *  |                                      |  *
    71   *  |   +-------+             +-------+    |  *
    72   *  |   | tap1  |             | tap2  |    |  *
    73   *  |   +---+---+             +---+---+    |  *
    74   *  |       |                     |        |  *
    75   *  +-------+------------------------------+  *
    76   *          |                     |           *
    77   *  +-------+---------+   +-------+--------+  *
    78   *  | linux-tap1      |   | linux-tap1     |  *
    79   *  | IP: 10.0.0.2/24 |   | IP: 20.0.0.2/24|  *
    80   *  +-----------------+   +----------------+  *
    81   *  VRF-dev-2			  VRF-dev-1           *
    82   *                                            *
    83   **********************************************/
    84  
    85  /* Vpp-agent Init and Close*/
    86  
    87  // Start Agent plugins selected for this example.
    88  func main() {
    89  	// Set inter-dependency between VPP & Linux plugins
    90  	vpp_ifplugin.DefaultPlugin.LinuxIfPlugin = &linux_ifplugin.DefaultPlugin
    91  	vpp_ifplugin.DefaultPlugin.NsPlugin = &linux_nsplugin.DefaultPlugin
    92  	linux_ifplugin.DefaultPlugin.VppIfPlugin = &vpp_ifplugin.DefaultPlugin
    93  
    94  	// Init close channel to stop the example.
    95  	exampleFinished := make(chan struct{})
    96  
    97  	// Inject dependencies to example plugin
    98  	ep := &VrfExamplePlugin{
    99  		Log:          logging.DefaultLogger,
   100  		VPP:          app.DefaultVPP(),
   101  		Linux:        app.DefaultLinux(),
   102  		Orchestrator: &orchestrator.DefaultPlugin,
   103  	}
   104  
   105  	// Start Agent
   106  	a := agent.NewAgent(
   107  		agent.AllPlugins(ep),
   108  		agent.QuitOnClose(exampleFinished),
   109  	)
   110  	if err := a.Run(); err != nil {
   111  		log.Fatal(err)
   112  	}
   113  
   114  	go closeExample("VRF localhost example finished", exampleFinished)
   115  }
   116  
   117  // Stop the agent with desired info message.
   118  func closeExample(message string, exampleFinished chan struct{}) {
   119  	time.Sleep(time.Duration(*timeout+5) * time.Second)
   120  	logrus.DefaultLogger().Info(message)
   121  	close(exampleFinished)
   122  }
   123  
   124  /* VRF Example */
   125  
   126  // VrfExamplePlugin uses localclient to transport example vrf, tap and its linux part
   127  // configuration to linuxplugin or VPP plugins
   128  type VrfExamplePlugin struct {
   129  	Log logging.Logger
   130  	app.VPP
   131  	app.Linux
   132  	Orchestrator *orchestrator.Plugin
   133  
   134  	wg             sync.WaitGroup
   135  	cancelResync   context.CancelFunc
   136  	cancelModified context.CancelFunc
   137  }
   138  
   139  // PluginName represents name of plugin.
   140  const PluginName = "vrf-example"
   141  
   142  // Init initializes example plugin.
   143  func (p *VrfExamplePlugin) Init() error {
   144  	// Logger
   145  	p.Log = logrus.DefaultLogger()
   146  	p.Log.SetLevel(logging.DebugLevel)
   147  	p.Log.Info("Initializing VRF device example")
   148  
   149  	// Flags
   150  	flag.Parse()
   151  	p.Log.Infof("Timeout between create and modify set to %d", *timeout)
   152  
   153  	p.Log.Info("VRF example initialization done")
   154  	return nil
   155  }
   156  
   157  // AfterInit initializes example plugin.
   158  func (p *VrfExamplePlugin) AfterInit() error {
   159  	// Apply initial Linux/VPP configuration
   160  	p.putInitialData()
   161  
   162  	// Schedule VRF resync
   163  	var ctx context.Context
   164  	ctx, p.cancelResync = context.WithCancel(context.Background())
   165  	p.wg.Add(1)
   166  	go p.resync(ctx, *timeout)
   167  
   168  	// Schedule reconfiguration
   169  	ctx, p.cancelModified = context.WithCancel(context.Background())
   170  	p.wg.Add(1)
   171  	go p.putModifiedData(ctx, *timeout*2)
   172  
   173  	return nil
   174  }
   175  
   176  // Close cleans up the resources.
   177  func (p *VrfExamplePlugin) Close() error {
   178  	p.cancelResync()
   179  	p.cancelModified()
   180  	p.wg.Wait()
   181  
   182  	p.Log.Info("Closed VRF plugin")
   183  	return nil
   184  }
   185  
   186  // String returns plugin name
   187  func (p *VrfExamplePlugin) String() string {
   188  	return PluginName
   189  }
   190  
   191  // Configure initial data
   192  func (p *VrfExamplePlugin) putInitialData() {
   193  	p.Log.Infof("Applying initial configuration")
   194  	err := localclient.DataResyncRequest(PluginName).
   195  		LinuxInterface(vrf1()).
   196  		LinuxInterface(vrf2()).
   197  		VppInterface(tap1()).
   198  		LinuxInterface(linuxTap1()).
   199  		VppInterface(tap2()).
   200  		LinuxInterface(linuxTap2()).
   201  		Send().ReceiveReply()
   202  	if err != nil {
   203  		p.Log.Errorf("Initial configuration failed: %v", err)
   204  	} else {
   205  		p.Log.Info("Initial configuration successful")
   206  	}
   207  }
   208  
   209  // Configure modified data
   210  // This step serves as a MTU check for various kernels. After the resync, vrf1's
   211  // MTU should be 65575 and vrf2's MTU 65536
   212  func (p *VrfExamplePlugin) resync(ctx context.Context, timeout int) {
   213  	select {
   214  	case <-time.After(time.Duration(timeout) * time.Second):
   215  		p.Log.Infof("Applying resync VRFs")
   216  		// Simulate configuration change after timeout
   217  		err := localclient.DataResyncRequest(PluginName).
   218  			LinuxInterface(vrf1()).
   219  			LinuxInterface(vrf2()).
   220  			VppInterface(tap1()).
   221  			LinuxInterface(linuxTap1()).
   222  			VppInterface(tap2()).
   223  			LinuxInterface(linuxTap2()).
   224  			Send().ReceiveReply()
   225  		if err != nil {
   226  			p.Log.Errorf("Resync failed: %v", err)
   227  		} else {
   228  			p.Log.Info("Resync successful")
   229  		}
   230  	case <-ctx.Done():
   231  		// Cancel the scheduled re-configuration.
   232  		p.Log.Info("Resync of configuration canceled")
   233  	}
   234  	p.wg.Done()
   235  }
   236  
   237  // Configure modified data
   238  func (p *VrfExamplePlugin) putModifiedData(ctx context.Context, timeout int) {
   239  	select {
   240  	case <-time.After(time.Duration(timeout) * time.Second):
   241  		p.Log.Infof("Applying modified configuration")
   242  		// Simulate configuration change after timeout
   243  		err := localclient.DataChangeRequest(PluginName).
   244  			Put().
   245  			LinuxInterface(modifiedLinuxTap1()).
   246  			LinuxInterface(modifiedLinuxTap2()).
   247  			Send().ReceiveReply()
   248  		if err != nil {
   249  			p.Log.Errorf("Modified configuration failed: %v", err)
   250  		} else {
   251  			p.Log.Info("Modified configuration successful")
   252  		}
   253  	case <-ctx.Done():
   254  		// Cancel the scheduled re-configuration.
   255  		p.Log.Info("Modification of configuration canceled")
   256  	}
   257  	p.wg.Done()
   258  }
   259  
   260  /* Example Data */
   261  
   262  func vrf1() *linux_intf.Interface {
   263  	return &linux_intf.Interface{
   264  		Name:    "vrf1",
   265  		Type:    linux_intf.Interface_VRF_DEVICE,
   266  		Enabled: true,
   267  		Link: &linux_intf.Interface_VrfDev{
   268  			VrfDev: &linux_intf.VrfDevLink{
   269  				RoutingTable: 1,
   270  			},
   271  		},
   272  	}
   273  }
   274  
   275  func vrf2() *linux_intf.Interface {
   276  	return &linux_intf.Interface{
   277  		Name:    "vrf2",
   278  		Type:    linux_intf.Interface_VRF_DEVICE,
   279  		Enabled: true,
   280  		Mtu:     65536, // simulate old kernel's default
   281  		Link: &linux_intf.Interface_VrfDev{
   282  			VrfDev: &linux_intf.VrfDevLink{
   283  				RoutingTable: 2,
   284  			},
   285  		},
   286  	}
   287  }
   288  
   289  func tap1() *vpp_intf.Interface {
   290  	return &vpp_intf.Interface{
   291  		Name:        "tap1",
   292  		Type:        vpp_intf.Interface_TAP,
   293  		Enabled:     true,
   294  		PhysAddress: "D5:BC:DC:12:E4:0E",
   295  		IpAddresses: []string{
   296  			"10.0.0.11/24",
   297  		},
   298  		Link: &vpp_intf.Interface_Tap{
   299  			Tap: &vpp_intf.TapLink{
   300  				Version: 2,
   301  			},
   302  		},
   303  	}
   304  }
   305  
   306  func linuxTap1() *linux_intf.Interface {
   307  	return &linux_intf.Interface{
   308  		Name:        "linux-tap1",
   309  		Type:        linux_intf.Interface_TAP_TO_VPP,
   310  		Enabled:     true,
   311  		PhysAddress: "88:88:88:88:88:88",
   312  		IpAddresses: []string{
   313  			"10.0.0.2/24",
   314  		},
   315  		HostIfName:         "tap_to_vpp1",
   316  		VrfMasterInterface: "vrf1",
   317  		Link: &linux_intf.Interface_Tap{
   318  			Tap: &linux_intf.TapLink{
   319  				VppTapIfName: "tap1",
   320  			},
   321  		},
   322  	}
   323  }
   324  
   325  func modifiedLinuxTap1() *linux_intf.Interface {
   326  	return &linux_intf.Interface{
   327  		Name:        "linux-tap1",
   328  		Type:        linux_intf.Interface_TAP_TO_VPP,
   329  		Enabled:     true,
   330  		PhysAddress: "88:88:88:88:88:88",
   331  		IpAddresses: []string{
   332  			"10.0.0.2/24",
   333  		},
   334  		HostIfName:         "tap_to_vpp1",
   335  		VrfMasterInterface: "vrf2",
   336  		Link: &linux_intf.Interface_Tap{
   337  			Tap: &linux_intf.TapLink{
   338  				VppTapIfName: "tap1",
   339  			},
   340  		},
   341  	}
   342  }
   343  
   344  func tap2() *vpp_intf.Interface {
   345  	return &vpp_intf.Interface{
   346  		Name:        "tap2",
   347  		Type:        vpp_intf.Interface_TAP,
   348  		Enabled:     true,
   349  		PhysAddress: "D5:BC:DC:12:E4:0E",
   350  		IpAddresses: []string{
   351  			"20.0.0.11/24",
   352  		},
   353  		Link: &vpp_intf.Interface_Tap{
   354  			Tap: &vpp_intf.TapLink{
   355  				Version: 2,
   356  			},
   357  		},
   358  	}
   359  }
   360  
   361  func linuxTap2() *linux_intf.Interface {
   362  	return &linux_intf.Interface{
   363  		Name:        "linux-tap2",
   364  		Type:        linux_intf.Interface_TAP_TO_VPP,
   365  		Enabled:     true,
   366  		PhysAddress: "88:88:88:88:88:88",
   367  		IpAddresses: []string{
   368  			"20.0.0.2/24",
   369  		},
   370  		HostIfName:         "tap_to_vpp2",
   371  		VrfMasterInterface: "vrf2",
   372  		Link: &linux_intf.Interface_Tap{
   373  			Tap: &linux_intf.TapLink{
   374  				VppTapIfName: "tap2",
   375  			},
   376  		},
   377  	}
   378  }
   379  
   380  func modifiedLinuxTap2() *linux_intf.Interface {
   381  	return &linux_intf.Interface{
   382  		Name:        "linux-tap2",
   383  		Type:        linux_intf.Interface_TAP_TO_VPP,
   384  		Enabled:     true,
   385  		PhysAddress: "88:88:88:88:88:88",
   386  		IpAddresses: []string{
   387  			"20.0.0.2/24",
   388  		},
   389  		HostIfName:         "tap_to_vpp2",
   390  		VrfMasterInterface: "vrf1",
   391  		Link: &linux_intf.Interface_Tap{
   392  			Tap: &linux_intf.TapLink{
   393  				VppTapIfName: "tap2",
   394  			},
   395  		},
   396  	}
   397  }