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 */