go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/ifplugin/vppcalls/vpp2202/watch_vppcalls.go (about)

     1  //  Copyright (c) 2022 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 vpp2202
    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/vpp2202/dhcp"
    28  	vpp_ifs "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/interface"
    29  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/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  }