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 }