go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/ifplugin/vppcalls/vpp2106/watch_vppcalls.go (about) 1 // Copyright (c) 2021 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 vpp2106 16 17 import ( 18 "context" 19 "net" 20 "os" 21 "strings" 22 "time" 23 24 "github.com/pkg/errors" 25 govppapi "go.fd.io/govpp/api" 26 27 vpp_dhcp "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/dhcp" 28 vpp_ifs "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/interface" 29 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/interface_types" 30 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls" 31 ) 32 33 var ( 34 // EventDeliverTimeout defines maximum time to deliver event upstream. 35 EventDeliverTimeout = time.Second 36 // NotifChanBufferSize defines size of notification channel buffer. 37 NotifChanBufferSize = 10 38 ) 39 40 func (h *InterfaceVppHandler) WatchInterfaceEvents(ctx context.Context, eventsCh chan<- *vppcalls.InterfaceEvent) error { 41 notifChan := make(chan govppapi.Message, NotifChanBufferSize) 42 43 // subscribe to SwInterfaceEvent notifications 44 sub, err := h.callsChannel.SubscribeNotification(notifChan, &vpp_ifs.SwInterfaceEvent{}) 45 if err != nil { 46 return errors.Errorf("subscribing to VPP notification (sw_interface_event) failed: %v", err) 47 } 48 unsub := func() { 49 if err := sub.Unsubscribe(); err != nil { 50 h.log.Warnf("unsubscribing VPP notification (sw_interface_event) failed: %v", err) 51 } 52 } 53 54 go func() { 55 h.log.Debugf("start watching interface events") 56 defer h.log.Debugf("done watching interface events (%v)", ctx.Err()) 57 58 for { 59 select { 60 case e, open := <-notifChan: 61 if !open { 62 h.log.Debugf("interface events channel was closed") 63 unsub() 64 return 65 } 66 67 ifEvent, ok := e.(*vpp_ifs.SwInterfaceEvent) 68 if !ok { 69 h.log.Debugf("unexpected notification type: %#v", ifEvent) 70 continue 71 } 72 73 // try to send event 74 select { 75 case eventsCh <- toInterfaceEvent(ifEvent): 76 // sent ok 77 case <-ctx.Done(): 78 unsub() 79 return 80 default: 81 // channel full send event in goroutine for later processing 82 go func() { 83 select { 84 case eventsCh <- toInterfaceEvent(ifEvent): 85 // sent ok 86 case <-time.After(EventDeliverTimeout): 87 h.log.Warnf("unable to deliver interface event, dropping it: %+v", ifEvent) 88 } 89 }() 90 } 91 case <-ctx.Done(): 92 unsub() 93 return 94 } 95 } 96 }() 97 98 // enable interface events from VPP 99 if _, err := h.interfaces.WantInterfaceEvents(ctx, &vpp_ifs.WantInterfaceEvents{ 100 PID: uint32(os.Getpid()), 101 EnableDisable: 1, 102 }); err != nil { 103 if errors.Is(err, govppapi.VPPApiError(govppapi.INVALID_REGISTRATION)) { 104 h.log.Warnf("already subscribed to interface events: %v", err) 105 return nil 106 } 107 return errors.Errorf("failed to watch interface events: %v", err) 108 } 109 110 return nil 111 } 112 113 func (h *InterfaceVppHandler) WatchDHCPLeases(ctx context.Context, leasesCh chan<- *vppcalls.Lease) error { 114 notifChan := make(chan govppapi.Message, NotifChanBufferSize) 115 116 // subscribe for receiving DHCPComplEvent notifications 117 sub, err := h.callsChannel.SubscribeNotification(notifChan, &vpp_dhcp.DHCPComplEvent{}) 118 if err != nil { 119 return errors.Errorf("subscribing to VPP notification (dhcp_compl_event) failed: %v", err) 120 } 121 unsub := func() { 122 if err := sub.Unsubscribe(); err != nil { 123 h.log.Warnf("unsubscribing VPP notification (dhcp_compl_event) failed: %v", err) 124 } 125 } 126 127 go func() { 128 h.log.Debugf("start watching DHCP leases") 129 defer h.log.Debugf("done watching DHCP lease (%v)", ctx.Err()) 130 131 for { 132 select { 133 case e, open := <-notifChan: 134 if !open { 135 h.log.Debugf("interface notification channel was closed") 136 unsub() 137 return 138 } 139 140 dhcpEvent, ok := e.(*vpp_dhcp.DHCPComplEvent) 141 if !ok { 142 h.log.Debugf("unexpected notification type: %#v", dhcpEvent) 143 continue 144 } 145 146 // try to send event 147 select { 148 case leasesCh <- toDHCPLease(dhcpEvent): 149 // sent ok 150 case <-ctx.Done(): 151 unsub() 152 return 153 default: 154 // channel full send event in goroutine for later processing 155 go func() { 156 select { 157 case leasesCh <- toDHCPLease(dhcpEvent): 158 // sent ok 159 case <-time.After(EventDeliverTimeout): 160 h.log.Warnf("unable to deliver DHCP lease event, dropping it: %+v", dhcpEvent) 161 } 162 }() 163 } 164 case <-ctx.Done(): 165 unsub() 166 return 167 } 168 } 169 }() 170 171 return nil 172 } 173 174 func toInterfaceEvent(ifEvent *vpp_ifs.SwInterfaceEvent) *vppcalls.InterfaceEvent { 175 event := &vppcalls.InterfaceEvent{ 176 SwIfIndex: uint32(ifEvent.SwIfIndex), 177 Deleted: ifEvent.Deleted, 178 } 179 if ifEvent.Flags&interface_types.IF_STATUS_API_FLAG_ADMIN_UP == interface_types.IF_STATUS_API_FLAG_ADMIN_UP { 180 event.AdminState = 1 181 } 182 if ifEvent.Flags&interface_types.IF_STATUS_API_FLAG_LINK_UP == interface_types.IF_STATUS_API_FLAG_LINK_UP { 183 event.LinkState = 1 184 } 185 return event 186 } 187 188 func toDHCPLease(dhcpEvent *vpp_dhcp.DHCPComplEvent) *vppcalls.Lease { 189 lease := dhcpEvent.Lease 190 return &vppcalls.Lease{ 191 SwIfIndex: uint32(lease.SwIfIndex), 192 State: uint8(lease.State), 193 Hostname: strings.TrimRight(lease.Hostname, "\x00"), 194 IsIPv6: lease.IsIPv6, 195 HostAddress: dhcpAddressToString(lease.HostAddress, uint32(lease.MaskWidth), lease.IsIPv6), 196 RouterAddress: dhcpAddressToString(lease.RouterAddress, uint32(lease.MaskWidth), lease.IsIPv6), 197 HostMac: net.HardwareAddr(lease.HostMac[:]).String(), 198 } 199 }