go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/puntplugin/vppcalls/vpp2202/punt_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  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/pkg/errors"
    22  
    23  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/interface_types"
    24  	vpp_ip "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/ip"
    25  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/ip_types"
    26  	vpp_punt "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/punt"
    27  	punt "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/punt"
    28  )
    29  
    30  const PuntSocketHeaderVersion = 1
    31  
    32  // Socket path from the VPP startup config file, returned when a punt socket
    33  // is retrieved. Limited to single entry as supported in the VPP.
    34  var vppConfigSocketPath string
    35  
    36  // AddPunt configures new punt entry
    37  func (h *PuntVppHandler) AddPunt(p *punt.ToHost) error {
    38  	return errors.Errorf("passive punt add is currently not available")
    39  }
    40  
    41  // DeletePunt removes punt entry
    42  func (h *PuntVppHandler) DeletePunt(p *punt.ToHost) error {
    43  	return errors.Errorf("passive punt del is currently not available")
    44  }
    45  
    46  // AddPuntException adds new punt exception entry
    47  func (h *PuntVppHandler) AddPuntException(p *punt.Exception) (string, error) {
    48  	return h.addDelPuntException(p, true)
    49  }
    50  
    51  // DeletePuntException removes punt exception entry
    52  func (h *PuntVppHandler) DeletePuntException(p *punt.Exception) error {
    53  	_, err := h.addDelPuntException(p, false)
    54  	return err
    55  }
    56  
    57  func (h *PuntVppHandler) addDelPuntException(p *punt.Exception, isAdd bool) (pathName string, err error) {
    58  	reasons, err := h.dumpPuntReasons()
    59  	if err != nil {
    60  		return "", fmt.Errorf("dumping punt reasons failed: %v", err)
    61  	}
    62  
    63  	h.log.Debugf("dumped %d punt reasons: %+v", len(reasons), reasons)
    64  
    65  	var reasonID *uint32
    66  	for _, r := range reasons {
    67  		if r.Reason.Name == p.Reason {
    68  			id := r.ID
    69  			reasonID = &id
    70  			break
    71  		}
    72  	}
    73  	if reasonID == nil {
    74  		return "", fmt.Errorf("punt reason %q not found", p.Reason)
    75  	}
    76  
    77  	baPunt := getPuntExceptionConfig(*reasonID)
    78  
    79  	if isAdd {
    80  		h.log.Debugf("adding punt exception: %+v", p)
    81  		pathName, err = h.handleRegisterPuntSocket(baPunt, p.SocketPath)
    82  		if err != nil {
    83  			return "", err
    84  		}
    85  	} else {
    86  		err = h.handleDeregisterPuntSocket(baPunt)
    87  		if err != nil {
    88  			return "", err
    89  		}
    90  	}
    91  
    92  	return pathName, nil
    93  }
    94  
    95  // RegisterPuntSocket registers new punt to unix domain socket entry
    96  func (h *PuntVppHandler) RegisterPuntSocket(p *punt.ToHost) (pathName string, err error) {
    97  	ipProto := resolveL4Proto(p.L4Protocol)
    98  
    99  	if p.L3Protocol == punt.L3Protocol_IPV4 || p.L3Protocol == punt.L3Protocol_ALL {
   100  		baPunt := getPuntL4Config(ip_types.ADDRESS_IP4, ipProto, uint16(p.Port))
   101  		if pathName, err = h.handleRegisterPuntSocket(baPunt, p.SocketPath); err != nil {
   102  			return "", err
   103  		}
   104  	}
   105  	if p.L3Protocol == punt.L3Protocol_IPV6 || p.L3Protocol == punt.L3Protocol_ALL {
   106  		baPunt := getPuntL4Config(ip_types.ADDRESS_IP6, ipProto, uint16(p.Port))
   107  		if pathName, err = h.handleRegisterPuntSocket(baPunt, p.SocketPath); err != nil {
   108  			return "", err
   109  		}
   110  	}
   111  
   112  	return pathName, nil
   113  }
   114  
   115  // DeregisterPuntSocket removes existing punt to socket registration
   116  func (h *PuntVppHandler) DeregisterPuntSocket(p *punt.ToHost) error {
   117  	ipProto := resolveL4Proto(p.L4Protocol)
   118  
   119  	if p.L3Protocol == punt.L3Protocol_IPV4 || p.L3Protocol == punt.L3Protocol_ALL {
   120  		baPunt := getPuntL4Config(ip_types.ADDRESS_IP4, ipProto, uint16(p.Port))
   121  		if err := h.handleDeregisterPuntSocket(baPunt); err != nil {
   122  			return err
   123  		}
   124  	}
   125  	if p.L3Protocol == punt.L3Protocol_IPV6 || p.L3Protocol == punt.L3Protocol_ALL {
   126  		baPunt := getPuntL4Config(ip_types.ADDRESS_IP6, ipProto, uint16(p.Port))
   127  		if err := h.handleDeregisterPuntSocket(baPunt); err != nil {
   128  			return err
   129  		}
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  func (h *PuntVppHandler) handleRegisterPuntSocket(punt vpp_punt.Punt, path string) (string, error) {
   136  	req := &vpp_punt.PuntSocketRegister{
   137  		HeaderVersion: PuntSocketHeaderVersion,
   138  		Punt:          punt,
   139  		Pathname:      path,
   140  	}
   141  	reply := &vpp_punt.PuntSocketRegisterReply{}
   142  
   143  	h.log.Debugf("registering punt socket: %+v (pathname: %s)", req.Punt, req.Pathname)
   144  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   145  		return "", err
   146  	}
   147  
   148  	// socket pathname from VPP config
   149  	pathName := strings.SplitN(string(reply.Pathname), "\x00", 2)[0]
   150  
   151  	// VPP startup config socket path name is always the same
   152  	if vppConfigSocketPath != pathName {
   153  		h.log.Debugf("setting vpp punt socket path to: %q (%s)", pathName, vppConfigSocketPath)
   154  		vppConfigSocketPath = pathName
   155  	}
   156  
   157  	return pathName, nil
   158  }
   159  
   160  // DeregisterPuntSocket removes existing punt to socket registration
   161  func (h *PuntVppHandler) handleDeregisterPuntSocket(punt vpp_punt.Punt) error {
   162  	req := &vpp_punt.PuntSocketDeregister{
   163  		Punt: punt,
   164  	}
   165  	reply := &vpp_punt.PuntSocketDeregisterReply{}
   166  
   167  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   168  		return err
   169  	}
   170  
   171  	return nil
   172  }
   173  
   174  func getPuntExceptionConfig(reasonID uint32) vpp_punt.Punt {
   175  	p := vpp_punt.PuntException{
   176  		ID: reasonID,
   177  	}
   178  	return vpp_punt.Punt{
   179  		Type: vpp_punt.PUNT_API_TYPE_EXCEPTION,
   180  		Punt: vpp_punt.PuntUnionException(p),
   181  	}
   182  }
   183  
   184  func getPuntL4Config(ipv ip_types.AddressFamily, ipProto ip_types.IPProto, port uint16) vpp_punt.Punt {
   185  	puntL4 := vpp_punt.PuntL4{
   186  		Af:       ipv,
   187  		Protocol: ipProto,
   188  		Port:     port,
   189  	}
   190  	return vpp_punt.Punt{
   191  		Type: vpp_punt.PUNT_API_TYPE_L4,
   192  		Punt: vpp_punt.PuntUnionL4(puntL4),
   193  	}
   194  }
   195  
   196  // AddPuntRedirect adds new redirect entry
   197  func (h *PuntVppHandler) AddPuntRedirect(puntCfg *punt.IPRedirect) error {
   198  	if puntCfg.L3Protocol == punt.L3Protocol_IPV4 || puntCfg.L3Protocol == punt.L3Protocol_ALL {
   199  		if err := h.handlePuntRedirectIPv4(puntCfg, true); err != nil {
   200  			return err
   201  		}
   202  	}
   203  	if puntCfg.L3Protocol == punt.L3Protocol_IPV6 || puntCfg.L3Protocol == punt.L3Protocol_ALL {
   204  		if err := h.handlePuntRedirectIPv6(puntCfg, true); err != nil {
   205  			return err
   206  		}
   207  	}
   208  	return nil
   209  }
   210  
   211  // DeletePuntRedirect removes existing redirect entry
   212  func (h *PuntVppHandler) DeletePuntRedirect(puntCfg *punt.IPRedirect) error {
   213  	if puntCfg.L3Protocol == punt.L3Protocol_IPV4 || puntCfg.L3Protocol == punt.L3Protocol_ALL {
   214  		if err := h.handlePuntRedirectIPv4(puntCfg, false); err != nil {
   215  			return err
   216  		}
   217  	}
   218  	if puntCfg.L3Protocol == punt.L3Protocol_IPV6 || puntCfg.L3Protocol == punt.L3Protocol_ALL {
   219  		if err := h.handlePuntRedirectIPv6(puntCfg, false); err != nil {
   220  			return err
   221  		}
   222  	}
   223  	return nil
   224  }
   225  
   226  func (h *PuntVppHandler) handlePuntRedirectIPv4(punt *punt.IPRedirect, isAdd bool) error {
   227  	return h.handlePuntRedirect(punt, true, isAdd)
   228  }
   229  
   230  func (h *PuntVppHandler) handlePuntRedirectIPv6(punt *punt.IPRedirect, isAdd bool) error {
   231  	return h.handlePuntRedirect(punt, false, isAdd)
   232  }
   233  
   234  func (h *PuntVppHandler) handlePuntRedirect(punt *punt.IPRedirect, isIPv4, isAdd bool) error {
   235  	// rx interface
   236  	var rxIfIdx uint32
   237  	if punt.RxInterface == "" {
   238  		rxIfIdx = ^uint32(0)
   239  	} else {
   240  		rxMetadata, exists := h.ifIndexes.LookupByName(punt.RxInterface)
   241  		if !exists {
   242  			return errors.Errorf("index not found for interface %s", punt.RxInterface)
   243  		}
   244  		rxIfIdx = rxMetadata.SwIfIndex
   245  	}
   246  
   247  	// tx interface
   248  	txMetadata, exists := h.ifIndexes.LookupByName(punt.TxInterface)
   249  	if !exists {
   250  		return errors.Errorf("index not found for interface %s", punt.TxInterface)
   251  	}
   252  
   253  	// next hop address
   254  	//  - remove mask from IP address if necessary
   255  	nextHopStr := punt.NextHop
   256  	ipParts := strings.Split(punt.NextHop, "/")
   257  	if len(ipParts) > 1 {
   258  		h.log.Debugf("IP punt redirect next hop IP address %s is defined with mask, removing it")
   259  		nextHopStr = ipParts[0]
   260  	}
   261  	nextHop, err := ipToAddress(nextHopStr)
   262  	if err != nil {
   263  		return err
   264  	}
   265  
   266  	req := &vpp_ip.IPPuntRedirect{
   267  		IsAdd: isAdd,
   268  		Punt: vpp_ip.PuntRedirect{
   269  			RxSwIfIndex: interface_types.InterfaceIndex(rxIfIdx),
   270  			TxSwIfIndex: interface_types.InterfaceIndex(txMetadata.SwIfIndex),
   271  			Nh:          nextHop,
   272  		},
   273  	}
   274  	reply := &vpp_ip.IPPuntRedirectReply{}
   275  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   276  		return err
   277  	}
   278  
   279  	return nil
   280  }
   281  
   282  func resolveL4Proto(protocol punt.L4Protocol) ip_types.IPProto {
   283  	if protocol == punt.L4Protocol_UDP {
   284  		return ip_types.IP_API_PROTO_UDP
   285  	}
   286  	return ip_types.IP_API_PROTO_TCP
   287  }