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