go.ligato.io/vpp-agent/v3@v3.5.0/examples/grpc_vpp/remote_client/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  	"fmt"
    20  	"log"
    21  	"net"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/namsral/flag"
    26  	"go.ligato.io/cn-infra/v2/agent"
    27  	"go.ligato.io/cn-infra/v2/infra"
    28  	"go.ligato.io/cn-infra/v2/logging/logrus"
    29  	"google.golang.org/grpc"
    30  	"google.golang.org/protobuf/encoding/protojson"
    31  
    32  	"go.ligato.io/vpp-agent/v3/proto/ligato/configurator"
    33  	"go.ligato.io/vpp-agent/v3/proto/ligato/linux"
    34  	linux_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
    35  	"go.ligato.io/vpp-agent/v3/proto/ligato/vpp"
    36  	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    37  	vpp_ipsec "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/ipsec"
    38  	vpp_l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3"
    39  )
    40  
    41  var (
    42  	address    = flag.String("address", "172.17.0.2:9111", "address of GRPC server")
    43  	socketType = flag.String("socket-type", "tcp", "socket type [tcp, tcp4, tcp6, unix, unixpacket]")
    44  
    45  	dialTimeout = time.Second * 2
    46  )
    47  
    48  var exampleFinished = make(chan struct{})
    49  
    50  func main() {
    51  	ep := &ExamplePlugin{}
    52  	ep.SetName("remote-client-example")
    53  	ep.Setup()
    54  
    55  	a := agent.NewAgent(
    56  		agent.AllPlugins(ep),
    57  		agent.QuitOnClose(exampleFinished),
    58  	)
    59  	if err := a.Run(); err != nil {
    60  		log.Fatal()
    61  	}
    62  }
    63  
    64  // ExamplePlugin demonstrates the use of the remoteclient to locally transport example configuration into the default VPP plugins.
    65  type ExamplePlugin struct {
    66  	infra.PluginDeps
    67  
    68  	conn *grpc.ClientConn
    69  
    70  	wg     sync.WaitGroup
    71  	cancel context.CancelFunc
    72  }
    73  
    74  // Init initializes example plugin.
    75  func (p *ExamplePlugin) Init() (err error) {
    76  	// Set up connection to the server.
    77  	p.conn, err = grpc.Dial("unix",
    78  		grpc.WithInsecure(),
    79  		grpc.WithDialer(dialer(*socketType, *address, dialTimeout)),
    80  	)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	client := configurator.NewConfiguratorServiceClient(p.conn)
    86  
    87  	// Apply initial VPP configuration.
    88  	go p.demonstrateClient(client)
    89  
    90  	// Schedule reconfiguration.
    91  	var ctx context.Context
    92  	ctx, p.cancel = context.WithCancel(context.Background())
    93  	_ = ctx
    94  	/*plugin.wg.Add(1)
    95  	go plugin.reconfigureVPP(ctx)*/
    96  
    97  	go func() {
    98  		time.Sleep(time.Second * 30)
    99  		close(exampleFinished)
   100  	}()
   101  
   102  	return nil
   103  }
   104  
   105  // Close cleans up the resources.
   106  func (p *ExamplePlugin) Close() error {
   107  	logrus.DefaultLogger().Info("Closing example plugin")
   108  
   109  	p.cancel()
   110  	p.wg.Wait()
   111  
   112  	if err := p.conn.Close(); err != nil {
   113  		return err
   114  	}
   115  
   116  	return nil
   117  }
   118  
   119  // demonstrateClient propagates snapshot of the whole initial configuration to VPP plugins.
   120  func (p *ExamplePlugin) demonstrateClient(client configurator.ConfiguratorServiceClient) {
   121  	time.Sleep(time.Second * 2)
   122  	p.Log.Infof("Requesting resync..")
   123  
   124  	config := &configurator.Config{
   125  		VppConfig: &vpp.ConfigData{
   126  			Interfaces: []*interfaces.Interface{
   127  				memif1,
   128  			},
   129  			IpscanNeighbor: ipScanNeigh,
   130  			IpsecSas:       []*vpp_ipsec.SecurityAssociation{sa10},
   131  			IpsecSpds:      []*vpp_ipsec.SecurityPolicyDatabase{spd1},
   132  			IpsecSps:       []*vpp_ipsec.SecurityPolicy{sp},
   133  		},
   134  		LinuxConfig: &linux.ConfigData{
   135  			Interfaces: []*linux_interfaces.Interface{
   136  				veth1, veth2,
   137  			},
   138  		},
   139  	}
   140  	_, err := client.Update(context.Background(), &configurator.UpdateRequest{
   141  		Update:     config,
   142  		FullResync: true,
   143  	})
   144  	if err != nil {
   145  		log.Fatalln(err)
   146  	}
   147  
   148  	time.Sleep(time.Second * 5)
   149  	p.Log.Infof("Requesting change..")
   150  
   151  	ifaces := []*interfaces.Interface{memif1, memif2, afpacket}
   152  	_, err = client.Update(context.Background(), &configurator.UpdateRequest{
   153  		Update: &configurator.Config{
   154  			VppConfig: &vpp.ConfigData{
   155  				Interfaces: ifaces,
   156  			},
   157  		},
   158  	})
   159  	if err != nil {
   160  		log.Fatalln(err)
   161  	}
   162  	time.Sleep(time.Second * 5)
   163  	p.Log.Infof("Requesting delete..")
   164  
   165  	ifaces = []*interfaces.Interface{memif1}
   166  	_, err = client.Delete(context.Background(), &configurator.DeleteRequest{
   167  		Delete: &configurator.Config{
   168  			VppConfig: &vpp.ConfigData{
   169  				Interfaces: ifaces,
   170  			},
   171  		},
   172  	})
   173  	if err != nil {
   174  		log.Fatalln(err)
   175  	}
   176  
   177  	time.Sleep(time.Second * 5)
   178  	p.Log.Infof("Requesting get..")
   179  
   180  	cfg, err := client.Get(context.Background(), &configurator.GetRequest{})
   181  	if err != nil {
   182  		log.Fatalln(err)
   183  	}
   184  	out := protojson.Format(cfg)
   185  	fmt.Printf("Config:\n %+v\n", out)
   186  
   187  	time.Sleep(time.Second * 5)
   188  	p.Log.Infof("Requesting dump..")
   189  
   190  	dump, err := client.Dump(context.Background(), &configurator.DumpRequest{})
   191  	if err != nil {
   192  		log.Fatalln(err)
   193  	}
   194  	fmt.Printf("Dump:\n %+v\n", protojson.Format(dump))
   195  }
   196  
   197  // Dialer for unix domain socket
   198  func dialer(socket, address string, timeoutVal time.Duration) func(string, time.Duration) (net.Conn, error) {
   199  	return func(addr string, timeout time.Duration) (net.Conn, error) {
   200  		// Pass values
   201  		addr, timeout = address, timeoutVal
   202  		// Dial with timeout
   203  		return net.DialTimeout(socket, addr, timeoutVal)
   204  	}
   205  }
   206  
   207  var (
   208  	sa10 = &vpp.IPSecSA{
   209  		Index:     10,
   210  		Spi:       1001,
   211  		Protocol:  vpp_ipsec.SecurityAssociation_ESP,
   212  		CryptoAlg: vpp_ipsec.CryptoAlg_AES_CBC_128,
   213  		CryptoKey: "4a506a794f574265564551694d653768",
   214  		IntegAlg:  vpp_ipsec.IntegAlg_SHA1_96,
   215  		IntegKey:  "4339314b55523947594d6d3547666b45764e6a58",
   216  	}
   217  	spd1 = &vpp.IPSecSPD{
   218  		Index: 1,
   219  	}
   220  	sp = &vpp.IPSecSP{
   221  		SpdIndex:        spd1.Index,
   222  		SaIndex:         sa10.Index,
   223  		Action:          vpp_ipsec.SecurityPolicy_BYPASS,
   224  		Priority:        100,
   225  		IsOutbound:      false,
   226  		Protocol:        50,
   227  		LocalAddrStart:  "192.168.1.1",
   228  		LocalAddrStop:   "192.168.1.255",
   229  		RemoteAddrStart: "192.168.2.1",
   230  		RemoteAddrStop:  "192.168.2.255",
   231  	}
   232  	memif1 = &vpp.Interface{
   233  		Name:        "memif1",
   234  		Enabled:     true,
   235  		IpAddresses: []string{"3.3.0.1/16"},
   236  		Type:        interfaces.Interface_MEMIF,
   237  		Link: &interfaces.Interface_Memif{
   238  			Memif: &interfaces.MemifLink{
   239  				Id:             1,
   240  				Master:         true,
   241  				Secret:         "secret",
   242  				SocketFilename: "/tmp/memif1.sock",
   243  			},
   244  		},
   245  	}
   246  	memif2 = &vpp.Interface{
   247  		Name:        "memif2",
   248  		Enabled:     true,
   249  		IpAddresses: []string{"4.3.0.1/16"},
   250  		Type:        interfaces.Interface_MEMIF,
   251  		Link: &interfaces.Interface_Memif{
   252  			Memif: &interfaces.MemifLink{
   253  				Id:             2,
   254  				Master:         true,
   255  				Secret:         "secret",
   256  				SocketFilename: "/tmp/memif2.sock",
   257  			},
   258  		},
   259  	}
   260  	ipScanNeigh = &vpp.IPScanNeigh{
   261  		Mode: vpp_l3.IPScanNeighbor_BOTH,
   262  	}
   263  	veth1 = &linux.Interface{
   264  		Name:        "myVETH1",
   265  		Type:        linux_interfaces.Interface_VETH,
   266  		Enabled:     true,
   267  		HostIfName:  "veth1",
   268  		IpAddresses: []string{"10.10.3.1/24"},
   269  		Link: &linux_interfaces.Interface_Veth{
   270  			Veth: &linux_interfaces.VethLink{
   271  				PeerIfName: "myVETH2",
   272  			},
   273  		},
   274  	}
   275  	veth2 = &linux.Interface{
   276  		Name:       "myVETH2",
   277  		Type:       linux_interfaces.Interface_VETH,
   278  		Enabled:    true,
   279  		HostIfName: "veth2",
   280  		Link: &linux_interfaces.Interface_Veth{
   281  			Veth: &linux_interfaces.VethLink{
   282  				PeerIfName: "myVETH1",
   283  			},
   284  		},
   285  	}
   286  	afpacket = &vpp.Interface{
   287  		Name:        "myAFpacket",
   288  		Type:        interfaces.Interface_AF_PACKET,
   289  		Enabled:     true,
   290  		PhysAddress: "a7:35:45:55:65:75",
   291  		IpAddresses: []string{
   292  			"10.20.30.40/24",
   293  		},
   294  		Mtu: 1800,
   295  		Link: &interfaces.Interface_Afpacket{
   296  			Afpacket: &interfaces.AfpacketLink{
   297  				HostIfName: "veth2",
   298  			},
   299  		},
   300  	}
   301  )
   302  
   303  /*
   304  // demonstrateClient propagates snapshot of the whole initial configuration to VPP plugins.
   305  func (plugin *ExamplePlugin) demonstrateClient() {
   306  	err := remoteclient.DataResyncRequestGRPC(rpc.NewDataResyncServiceClient(plugin.conn)).
   307  		Interface(&memif1AsMaster).
   308  		Interface(&tap1Disabled).
   309  		Interface(&loopback1).
   310  		StaticRoute(&routeThroughMemif1).
   311  		Send().ReceiveReply()
   312  	if err != nil {
   313  		logrus.DefaultLogger().Errorf("Failed to apply initial VPP configuration: %v", err)
   314  	} else {
   315  		logrus.DefaultLogger().Info("Successfully applied initial VPP configuration")
   316  	}
   317  }
   318  
   319  // reconfigureVPP simulates a set of changes in the configuration related to VPP plugins.
   320  func (plugin *ExamplePlugin) reconfigureVPP(ctx context.Context) {
   321  	return
   322  	_, dstNetAddr, err := net.ParseCIDR("192.168.2.1/32")
   323  	if err != nil {
   324  		return
   325  	}
   326  	nextHopAddr := net.ParseIP("192.168.1.1")
   327  
   328  	select {
   329  	case <-time.After(3 * time.Second):
   330  		// Simulate configuration change exactly 15seconds after resync.
   331  		err := remoteclient.DataChangeRequestGRPC(rpc.NewDataChangeServiceClient(plugin.conn)).
   332  			Put().
   333  			Interface(&memif1AsSlave).
   334  			Interface(&memif2).
   335  			Interface(&tap1Enabled).
   336  			Interface(&loopback1WithAddr).
   337  			ACL(&acl1).
   338  			XConnect(&XConMemif1ToMemif2).
   339  			BD(&BDLoopback1ToTap1).
   340  			Delete().
   341  			StaticRoute(0, dstNetAddr.String(), nextHopAddr.String()).
   342  			Send().ReceiveReply()
   343  		if err != nil {
   344  			logrus.DefaultLogger().Errorf("Failed to reconfigure VPP: %v", err)
   345  		} else {
   346  			logrus.DefaultLogger().Info("Successfully reconfigured VPP")
   347  		}
   348  	case <-ctx.Done():
   349  		// Cancel the scheduled re-configuration.
   350  		logrus.DefaultLogger().Info("Planned VPP re-configuration was canceled")
   351  	}
   352  	plugin.wg.Done()
   353  }
   354  */
   355  /*************************
   356   * Example plugin config *
   357   *************************/
   358  
   359  /*****************************************************
   360   * After Resync                                      *
   361   *                                                   *
   362   *  +---------------------------------------------+  *
   363   *  |                                             |  *
   364   *  +-----------+           +---------------------+  *
   365   *  | tap1      |           |  memif1             |  *
   366   *  | DISABLED  |      +--> |  MASTER             |  *
   367   *  +-----------+      |    |  IP: 192.168.1.1/24 |  *
   368   *  |                  |    +---------------------+  *
   369   *  |  +-----------+   |                          |  *
   370   *  |  | loopback1 |   +                          |  *
   371   *  |  +-----------+   route for 192.168.2.1      |  *
   372   *  |                                             |  *
   373   *  +---------------------------------------------+  *
   374   *                                                   *
   375   *****************************************************/
   376  
   377  /********************************************************
   378   * After Data Change Request                            *
   379   *                                                      *
   380   *  +------------------------------------------------+  *
   381   *  |                                                |  *
   382   *  +---------+ +------+                  +----------+  *
   383   *  | tap1    |-| acl1 |-+         +------| memif1   |  *
   384   *  | ENABLED | +------+ |         |      | SLAVE    |  *
   385   *  +---------+          |         |      +----------+  *
   386   *  |                  Bridge   xconnect             |  *
   387   *  |                  domain      |      +----------+  *
   388   *  |                    |         |      | memif2   |  *
   389   *  |  +------------+    |         +------| SLAVE    |  *
   390   *  |  | loopback1  |----+                +----------|  *
   391   *  |  +------------+                                |  *
   392   *  |                                                |  *
   393   *  +------------------------------------------------+  *
   394   *                                                      *
   395   ********************************************************/
   396  /*
   397  var (
   398  	// memif1AsMaster is an example of a memory interface configuration. (Master=true, with IPv4 address).
   399  	memif1AsMaster = interfaces.Interfaces_Interface{
   400  		Name:    "memif1",
   401  		Type:    interfaces.InterfaceType_MEMORY_INTERFACE,
   402  		Enabled: true,
   403  		Memif: &interfaces.Interfaces_Interface_Memif{
   404  			Id:             1,
   405  			Master:         true,
   406  			SocketFilename: "/tmp/memif1.sock",
   407  		},
   408  		Mtu:         1500,
   409  		IpAddresses: []string{"192.168.1.1/24"},
   410  	}
   411  
   412  	// memif1AsSlave is the original memif1 turned into slave and stripped of the IP address.
   413  	memif1AsSlave = interfaces.Interfaces_Interface{
   414  		Name:    "memif1",
   415  		Type:    interfaces.InterfaceType_MEMORY_INTERFACE,
   416  		Enabled: true,
   417  		Memif: &interfaces.Interfaces_Interface_Memif{
   418  			Id:             1,
   419  			Master:         false,
   420  			SocketFilename: "/tmp/memif1.sock",
   421  		},
   422  		Mtu: 1500,
   423  	}
   424  
   425  	// Memif2 is a slave memif without IP address and to be xconnected with memif1.
   426  	memif2 = interfaces.Interfaces_Interface{
   427  		Name:    "memif2",
   428  		Type:    interfaces.InterfaceType_MEMORY_INTERFACE,
   429  		Enabled: true,
   430  		Memif: &interfaces.Interfaces_Interface_Memif{
   431  			Id:             2,
   432  			Master:         false,
   433  			SocketFilename: "/tmp/memif2.sock",
   434  		},
   435  		Mtu: 1500,
   436  	}
   437  	// XConMemif1ToMemif2 defines xconnect between memifs.
   438  	XConMemif1ToMemif2 = l2.XConnectPairs_XConnectPair{
   439  		ReceiveInterface:  memif1AsSlave.Name,
   440  		TransmitInterface: memif2.Name,
   441  	}
   442  
   443  	// tap1Disabled is a disabled tap interface.
   444  	tap1Disabled = interfaces.Interfaces_Interface{
   445  		Name:    "tap1",
   446  		Type:    interfaces.InterfaceType_TAP_INTERFACE,
   447  		Enabled: false,
   448  		Tap: &interfaces.Interfaces_Interface_Tap{
   449  			HostIfName: "linux-tap1",
   450  		},
   451  		Mtu: 1500,
   452  	}
   453  
   454  	// tap1Enabled is an enabled tap1 interface.
   455  	tap1Enabled = interfaces.Interfaces_Interface{
   456  		Name:    "tap1",
   457  		Type:    interfaces.InterfaceType_TAP_INTERFACE,
   458  		Enabled: true,
   459  		Tap: &interfaces.Interfaces_Interface_Tap{
   460  			HostIfName: "linux-tap1",
   461  		},
   462  		Mtu: 1500,
   463  	}
   464  
   465  	acl1 = acl.AccessLists_Acl{
   466  		AclName: "acl1",
   467  		Rules: []*acl.AccessLists_Acl_Rule{
   468  			{
   469  				RuleName:  "rule1",
   470  				AclAction: acl.AclAction_DENY,
   471  				Match: &acl.AccessLists_Acl_Rule_Match{
   472  					IpRule: &acl.AccessLists_Acl_Rule_Match_IpRule{
   473  						Ip: &acl.AccessLists_Acl_Rule_Match_IpRule_Ip{
   474  							DestinationNetwork: "10.1.1.0/24",
   475  							SourceNetwork:      "10.1.2.0/24",
   476  						},
   477  						Tcp: &acl.AccessLists_Acl_Rule_Match_IpRule_Tcp{
   478  							DestinationPortRange: &acl.AccessLists_Acl_Rule_Match_IpRule_PortRange{
   479  								LowerPort: 50,
   480  								UpperPort: 150,
   481  							},
   482  							SourcePortRange: &acl.AccessLists_Acl_Rule_Match_IpRule_PortRange{
   483  								LowerPort: 1000,
   484  								UpperPort: 2000,
   485  							},
   486  						},
   487  					},
   488  				},
   489  			},
   490  		},
   491  		Interfaces: &acl.AccessLists_Acl_Interfaces{
   492  			Egress: []string{"tap1"},
   493  		},
   494  	}
   495  
   496  	// loopback1 is an example of a loopback interface configuration (without IP address assigned).
   497  	loopback1 = interfaces.Interfaces_Interface{
   498  		Name:    "loopback1",
   499  		Type:    interfaces.InterfaceType_SOFTWARE_LOOPBACK,
   500  		Enabled: true,
   501  		Mtu:     1500,
   502  	}
   503  
   504  	// loopback1WithAddr extends loopback1 definition with an IP address.
   505  	loopback1WithAddr = interfaces.Interfaces_Interface{
   506  		Name:        "loopback1",
   507  		Type:        interfaces.InterfaceType_SOFTWARE_LOOPBACK,
   508  		Enabled:     true,
   509  		Mtu:         1500,
   510  		IpAddresses: []string{"10.0.0.1/24"},
   511  	}
   512  
   513  	// BDLoopback1ToTap1 is a bridge domain with tap1 and loopback1 interfaces in it.
   514  	// Loopback is set to be BVI.
   515  	BDLoopback1ToTap1 = l2.BridgeDomains_BridgeDomain{
   516  		Name:                "br1",
   517  		Flood:               false,
   518  		UnknownUnicastFlood: false,
   519  		Forward:             true,
   520  		Learn:               true,
   521  		ArpTermination:      false,
   522  		MacAge:              0,
   523  		Interfaces: []*l2.BridgeDomains_BridgeDomain_Interfaces{
   524  			{
   525  				Name: "loopback1",
   526  				BridgedVirtualInterface: true,
   527  			}, {
   528  				Name: "tap1",
   529  				BridgedVirtualInterface: false,
   530  			},
   531  		},
   532  	}
   533  
   534  	// routeThroughMemif1 is an example route configuration, with memif1 being the next hop.
   535  	routeThroughMemif1 = l3.StaticRoutes_Route{
   536  		Description: "Description",
   537  		VrfId:       0,
   538  		DstIpAddr:   "192.168.2.1/32",
   539  		NextHopAddr: "192.168.1.1", // Memif1AsMaster
   540  		Weight:      5,
   541  	}
   542  )
   543  */