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

     1  // Copyright (c) 2017 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  	linux_namespace "go.ligato.io/vpp-agent/v3/proto/ligato/linux/namespace"
    36  	vpp_intf "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    37  	vpp_l2 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l2"
    38  )
    39  
    40  var (
    41  	timeout = flag.Int("timeout", 20, "Timeout between applying of initial and modified configuration in seconds")
    42  )
    43  
    44  /* Confgiuration */
    45  
    46  // Initial Data configures TAP interface on the vpp with other end in the default namespace. Linux-tap is then set with
    47  // IP address. Also bridge domain is created for linux TAP interfaces
    48  /**********************************************
    49   * Initial Data                               *
    50   *                                            *
    51   *  +--------------------------------------+  *
    52   *  |       +-- Bridge Domain --+          |  *
    53   *  |       |                   |          |  *
    54   *  |   +-------+               |          |  *
    55   *  |   | tap1  |             (tap2)       |  *
    56   *  |   +---+---+                          |  *
    57   *  |       |                              |  *
    58   *  +-------+------------------------------+  *
    59   *          |                                 *
    60   *  +-------+---------+                       *
    61   *  | linux-tap1      |                       *
    62   *  | IP: 10.0.0.2/24 |                       *
    63   *  +-----------------+                       *
    64   *                                            *
    65   *                                            *
    66   **********************************************/
    67  
    68  // Modify sets IP address for tap1, moves linux host to namespace ns1 and configures second TAP interface with linux
    69  // host in namespace ns2
    70  /************************************************
    71   * Modified Data                                *
    72   *                                              *
    73   *  +----------------------------------------+  *
    74   *  |       +-- Bridge Domain --+            |  *
    75   *  |       |                   |            |  *
    76   *  | +-----+--------+    *------+-------+   |  *
    77   *  | | tap1         |    | (tap2)       |   |  *
    78   *  | | 10.0.0.11/24 |    | 20.0.0.11/24 |   |  *
    79   *  | +-----+--------+    +------+-------+   |  *
    80   *  |       |                   |            |  *
    81   *  +-------+-------------------+------------+  *
    82   *          |                   |               *
    83   *  +-------+----------+   +-----+------------+ *
    84   *  | linux-tap1       |   | linux-tap2       | *
    85   *  | IP: 10.0.0.12/24 |   | IP: 20.0.0.12\24 | *
    86   *  | Namespace: ns1   |   | Namespace: ns2   | *
    87   *  +------------------+   +------------------+ *
    88   *                                              *
    89   *                                              *
    90   ************************************************/
    91  
    92  /* Vpp-agent Init and Close*/
    93  
    94  // Start Agent plugins selected for this example.
    95  func main() {
    96  	// Set inter-dependency between VPP & Linux plugins
    97  	vpp_ifplugin.DefaultPlugin.LinuxIfPlugin = &linux_ifplugin.DefaultPlugin
    98  	vpp_ifplugin.DefaultPlugin.NsPlugin = &linux_nsplugin.DefaultPlugin
    99  	linux_ifplugin.DefaultPlugin.VppIfPlugin = &vpp_ifplugin.DefaultPlugin
   100  
   101  	// Init close channel to stop the example.
   102  	exampleFinished := make(chan struct{})
   103  
   104  	// Inject dependencies to example plugin
   105  	ep := &TapExamplePlugin{
   106  		Log:          logging.DefaultLogger,
   107  		VPP:          app.DefaultVPP(),
   108  		Linux:        app.DefaultLinux(),
   109  		Orchestrator: &orchestrator.DefaultPlugin,
   110  	}
   111  
   112  	// Start Agent
   113  	a := agent.NewAgent(
   114  		agent.AllPlugins(ep),
   115  		agent.QuitOnClose(exampleFinished),
   116  	)
   117  	if err := a.Run(); err != nil {
   118  		log.Fatal(err)
   119  	}
   120  
   121  	go closeExample("localhost example finished", exampleFinished)
   122  }
   123  
   124  // Stop the agent with desired info message.
   125  func closeExample(message string, exampleFinished chan struct{}) {
   126  	time.Sleep(time.Duration(*timeout+5) * time.Second)
   127  	logrus.DefaultLogger().Info(message)
   128  	close(exampleFinished)
   129  }
   130  
   131  /* TAP Example */
   132  
   133  // TapExamplePlugin uses localclient to transport example tap and its linux end
   134  // configuration to linuxplugin or VPP plugins
   135  type TapExamplePlugin struct {
   136  	Log logging.Logger
   137  	app.VPP
   138  	app.Linux
   139  	Orchestrator *orchestrator.Plugin
   140  
   141  	wg     sync.WaitGroup
   142  	cancel context.CancelFunc
   143  }
   144  
   145  // PluginName represents name of plugin.
   146  const PluginName = "tap-example"
   147  
   148  // Init initializes example plugin.
   149  func (p *TapExamplePlugin) Init() error {
   150  	// Logger
   151  	p.Log = logrus.DefaultLogger()
   152  	p.Log.SetLevel(logging.DebugLevel)
   153  	p.Log.Info("Initializing Tap example")
   154  
   155  	// Flags
   156  	flag.Parse()
   157  	p.Log.Infof("Timeout between create and modify set to %d", *timeout)
   158  
   159  	p.Log.Info("Tap example initialization done")
   160  	return nil
   161  }
   162  
   163  // AfterInit initializes example plugin.
   164  func (p *TapExamplePlugin) AfterInit() error {
   165  	// Apply initial Linux/VPP configuration.
   166  	p.putInitialData()
   167  
   168  	// Schedule reconfiguration.
   169  	var ctx context.Context
   170  	ctx, p.cancel = context.WithCancel(context.Background())
   171  	p.wg.Add(1)
   172  	go p.putModifiedData(ctx, *timeout)
   173  
   174  	return nil
   175  }
   176  
   177  // Close cleans up the resources.
   178  func (p *TapExamplePlugin) Close() error {
   179  	p.cancel()
   180  	p.wg.Wait()
   181  
   182  	p.Log.Info("Closed Tap plugin")
   183  	return nil
   184  }
   185  
   186  // String returns plugin name
   187  func (p *TapExamplePlugin) String() string {
   188  	return PluginName
   189  }
   190  
   191  // Configure initial data
   192  func (p *TapExamplePlugin) putInitialData() {
   193  	p.Log.Infof("Applying initial configuration")
   194  	err := localclient.DataResyncRequest(PluginName).
   195  		VppInterface(initialTap1()).
   196  		LinuxInterface(initialLinuxTap1()).
   197  		BD(bridgeDomain()).
   198  		Send().ReceiveReply()
   199  	if err != nil {
   200  		p.Log.Errorf("Initial configuration failed: %v", err)
   201  	} else {
   202  		p.Log.Info("Initial configuration successful")
   203  	}
   204  }
   205  
   206  // Configure modified data
   207  func (p *TapExamplePlugin) putModifiedData(ctx context.Context, timeout int) {
   208  	select {
   209  	case <-time.After(time.Duration(timeout) * time.Second):
   210  		p.Log.Infof("Applying modified configuration")
   211  		// Simulate configuration change after timeout
   212  		err := localclient.DataChangeRequest(PluginName).
   213  			Put().
   214  			VppInterface(modifiedTap1()).
   215  			VppInterface(tap2()).
   216  			LinuxInterface(modifiedLinuxTap1()).
   217  			LinuxInterface(linuxTap2()).
   218  			Send().ReceiveReply()
   219  		if err != nil {
   220  			p.Log.Errorf("Modified configuration failed: %v", err)
   221  		} else {
   222  			p.Log.Info("Modified configuration successful")
   223  		}
   224  	case <-ctx.Done():
   225  		// Cancel the scheduled re-configuration.
   226  		p.Log.Info("Modification of configuration canceled")
   227  	}
   228  	p.wg.Done()
   229  }
   230  
   231  /* Example Data */
   232  
   233  func initialTap1() *vpp_intf.Interface {
   234  	return &vpp_intf.Interface{
   235  		Name:    "tap1",
   236  		Type:    vpp_intf.Interface_TAP,
   237  		Enabled: true,
   238  		Link: &vpp_intf.Interface_Tap{
   239  			Tap: &vpp_intf.TapLink{
   240  				Version: 2,
   241  			},
   242  		},
   243  	}
   244  }
   245  
   246  func modifiedTap1() *vpp_intf.Interface {
   247  	return &vpp_intf.Interface{
   248  		Name:        "tap1",
   249  		Type:        vpp_intf.Interface_TAP,
   250  		Enabled:     true,
   251  		PhysAddress: "12:E4:0E:D5:BC:DC",
   252  		IpAddresses: []string{
   253  			"10.0.0.11/24",
   254  		},
   255  		Link: &vpp_intf.Interface_Tap{
   256  			Tap: &vpp_intf.TapLink{
   257  				Version: 2,
   258  			},
   259  		},
   260  	}
   261  }
   262  
   263  func tap2() *vpp_intf.Interface {
   264  	return &vpp_intf.Interface{
   265  		Name:        "tap2",
   266  		Type:        vpp_intf.Interface_TAP,
   267  		Enabled:     true,
   268  		PhysAddress: "D5:BC:DC:12:E4:0E",
   269  		IpAddresses: []string{
   270  			"20.0.0.11/24",
   271  		},
   272  		Link: &vpp_intf.Interface_Tap{
   273  			Tap: &vpp_intf.TapLink{
   274  				Version: 2,
   275  			},
   276  		},
   277  	}
   278  }
   279  
   280  func initialLinuxTap1() *linux_intf.Interface {
   281  	return &linux_intf.Interface{
   282  		Name:        "linux-tap1",
   283  		Type:        linux_intf.Interface_TAP_TO_VPP,
   284  		Enabled:     true,
   285  		PhysAddress: "88:88:88:88:88:88",
   286  		IpAddresses: []string{
   287  			"10.0.0.2/24",
   288  		},
   289  		HostIfName: "tap_to_vpp1",
   290  		Link: &linux_intf.Interface_Tap{
   291  			Tap: &linux_intf.TapLink{
   292  				VppTapIfName: "tap1",
   293  			},
   294  		},
   295  	}
   296  }
   297  
   298  func modifiedLinuxTap1() *linux_intf.Interface {
   299  	return &linux_intf.Interface{
   300  
   301  		Name:        "linux-tap1",
   302  		Type:        linux_intf.Interface_TAP_TO_VPP,
   303  		Enabled:     true,
   304  		PhysAddress: "BC:FE:E9:5E:07:04",
   305  		Namespace: &linux_namespace.NetNamespace{
   306  			Reference: "ns1",
   307  			Type:      linux_namespace.NetNamespace_NSID,
   308  		},
   309  		Mtu: 1500,
   310  		IpAddresses: []string{
   311  			"10.0.0.12/24",
   312  		},
   313  		HostIfName: "tap_to_vpp1",
   314  		Link: &linux_intf.Interface_Tap{
   315  			Tap: &linux_intf.TapLink{
   316  				VppTapIfName: "tap1",
   317  			},
   318  		},
   319  	}
   320  }
   321  
   322  func linuxTap2() *linux_intf.Interface {
   323  	return &linux_intf.Interface{
   324  
   325  		Name:        "linux-tap2",
   326  		Type:        linux_intf.Interface_TAP_TO_VPP,
   327  		Enabled:     true,
   328  		PhysAddress: "5E:07:04:BC:FE:E9",
   329  		Namespace: &linux_namespace.NetNamespace{
   330  			Reference: "ns2",
   331  			Type:      linux_namespace.NetNamespace_NSID,
   332  		},
   333  		Mtu: 1500,
   334  		IpAddresses: []string{
   335  			"20.0.0.12/24",
   336  		},
   337  		HostIfName: "tap_to_vpp2",
   338  		Link: &linux_intf.Interface_Tap{
   339  			Tap: &linux_intf.TapLink{
   340  				VppTapIfName: "tap2",
   341  			},
   342  		},
   343  	}
   344  }
   345  
   346  func bridgeDomain() *vpp_l2.BridgeDomain {
   347  	return &vpp_l2.BridgeDomain{
   348  		Name:                "br1",
   349  		Flood:               true,
   350  		UnknownUnicastFlood: true,
   351  		Forward:             true,
   352  		Learn:               true,
   353  		ArpTermination:      false,
   354  		MacAge:              0, /* means disable aging */
   355  		Interfaces: []*vpp_l2.BridgeDomain_Interface{
   356  			{
   357  				Name:                    "tap1",
   358  				BridgedVirtualInterface: false,
   359  			},
   360  			{
   361  				Name:                    "tap2",
   362  				BridgedVirtualInterface: false,
   363  			},
   364  			{
   365  				Name:                    "loop1",
   366  				BridgedVirtualInterface: true,
   367  			},
   368  		},
   369  	}
   370  }