github.com/elfadel/cilium@v1.6.12/pkg/proxy/proxy.go (about)

     1  // Copyright 2016-2018 Authors of Cilium
     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 proxy
    16  
    17  import (
    18  	"fmt"
    19  	"math/rand"
    20  	"time"
    21  
    22  	"github.com/cilium/cilium/api/v1/models"
    23  	"github.com/cilium/cilium/pkg/completion"
    24  	"github.com/cilium/cilium/pkg/envoy"
    25  	"github.com/cilium/cilium/pkg/lock"
    26  	"github.com/cilium/cilium/pkg/logging"
    27  	"github.com/cilium/cilium/pkg/logging/logfields"
    28  	"github.com/cilium/cilium/pkg/metrics"
    29  	"github.com/cilium/cilium/pkg/node"
    30  	"github.com/cilium/cilium/pkg/policy"
    31  	"github.com/cilium/cilium/pkg/proxy/logger"
    32  	"github.com/cilium/cilium/pkg/revert"
    33  
    34  	"github.com/sirupsen/logrus"
    35  )
    36  
    37  var (
    38  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "proxy")
    39  )
    40  
    41  // field names used while logging
    42  const (
    43  	fieldMarker          = "marker"
    44  	fieldSocket          = "socket"
    45  	fieldFd              = "fd"
    46  	fieldProxyRedirectID = "id"
    47  
    48  	// portReuseDelay is the delay until a port is being reused
    49  	portReuseDelay = 5 * time.Minute
    50  
    51  	// redirectCreationAttempts is the number of attempts to create a redirect
    52  	redirectCreationAttempts = 5
    53  )
    54  
    55  type DatapathUpdater interface {
    56  	InstallProxyRules(proxyPort uint16, ingress bool, name string) error
    57  	RemoveProxyRules(proxyPort uint16, ingress bool, name string) error
    58  	SupportsOriginalSourceAddr() bool
    59  }
    60  
    61  type ProxyPort struct {
    62  	// Listener name (immutable)
    63  	name string
    64  	// isStatic is true when the listener on the proxy port is incapable
    65  	// of stopping and/or being reconfigured with a new proxy port once it has been
    66  	// first started. Set 'true' by SetProxyPort(), which is only called for
    67  	// static listeners (currently only DNS proxy).
    68  	isStatic bool
    69  	// parser type this port applies to (immutable)
    70  	parserType policy.L7ParserType
    71  	// 'true' for ingress, 'false' for egress (immutable)
    72  	ingress bool
    73  	// ProxyPort is the desired proxy listening port number.
    74  	proxyPort uint16
    75  	// nRedirects is the number of redirects using this proxy port
    76  	nRedirects int
    77  	// Configured is true when the proxy is (being) configured, but not necessarily
    78  	// acknowledged yet. This is reset to false when the underlying proxy listener
    79  	// is removed.
    80  	configured bool
    81  	// rulesPort congains the proxy port value configured to the datapath rules and
    82  	// is non-zero when a proxy has been successfully created and the
    83  	// datapath rules have been created.
    84  	rulesPort uint16
    85  }
    86  
    87  // Proxy maintains state about redirects
    88  type Proxy struct {
    89  	*envoy.XDSServer
    90  
    91  	// stateDir is the path of the directory where the state of L7 proxies is
    92  	// stored.
    93  	stateDir string
    94  
    95  	// mutex is the lock required when modifying any proxy datastructure
    96  	mutex lock.RWMutex
    97  
    98  	// rangeMin is the minimum port used for proxy port allocation
    99  	rangeMin uint16
   100  
   101  	// rangeMax is the maximum port used for proxy port allocation.
   102  	// If port is unspecified, the proxy will automatically allocate
   103  	// ports out of the rangeMin-rangeMax range.
   104  	rangeMax uint16
   105  
   106  	// redirects is the map of all redirect configurations indexed by
   107  	// the redirect identifier. Redirects may be implemented by different
   108  	// proxies.
   109  	redirects map[string]*Redirect
   110  
   111  	// Datapath updater for installing and removing proxy rules for a single
   112  	// proxy port
   113  	datapathUpdater DatapathUpdater
   114  }
   115  
   116  // StartProxySupport starts the servers to support L7 proxies: xDS GRPC server
   117  // and access log server.
   118  func StartProxySupport(minPort uint16, maxPort uint16, stateDir string,
   119  	accessLogFile string, accessLogNotifier logger.LogRecordNotifier, accessLogMetadata []string,
   120  	datapathUpdater DatapathUpdater) *Proxy {
   121  	xdsServer := envoy.StartXDSServer(stateDir)
   122  
   123  	if accessLogFile != "" {
   124  		if err := logger.OpenLogfile(accessLogFile); err != nil {
   125  			log.WithError(err).WithField(logfields.Path, accessLogFile).
   126  				Warn("Cannot open L7 access log")
   127  		}
   128  	}
   129  
   130  	if accessLogNotifier != nil {
   131  		logger.SetNotifier(accessLogNotifier)
   132  	}
   133  
   134  	if len(accessLogMetadata) > 0 {
   135  		logger.SetMetadata(accessLogMetadata)
   136  	}
   137  
   138  	envoy.StartAccessLogServer(stateDir, xdsServer, DefaultEndpointInfoRegistry)
   139  
   140  	return &Proxy{
   141  		XDSServer:       xdsServer,
   142  		stateDir:        stateDir,
   143  		rangeMin:        minPort,
   144  		rangeMax:        maxPort,
   145  		redirects:       make(map[string]*Redirect),
   146  		datapathUpdater: datapathUpdater,
   147  	}
   148  }
   149  
   150  var (
   151  	// proxyPortsMutex protects access to allocatedPorts, portRandomized, and proxyPorts
   152  	proxyPortsMutex lock.Mutex
   153  
   154  	// allocatedPorts is the map of all allocated proxy ports
   155  	allocatedPorts = make(map[uint16]struct{})
   156  
   157  	portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano()))
   158  
   159  	// proxyPorts is a slice of all supported proxy ports
   160  	// The number and order of entries are fixed, and the fields
   161  	// initialized here are immutable.
   162  	proxyPorts = []ProxyPort{
   163  		{
   164  			parserType: policy.ParserTypeHTTP,
   165  			ingress:    false,
   166  			name:       "cilium-http-egress",
   167  		},
   168  		{
   169  			parserType: policy.ParserTypeHTTP,
   170  			ingress:    true,
   171  			name:       "cilium-http-ingress",
   172  		},
   173  		{
   174  			parserType: policy.ParserTypeKafka,
   175  			ingress:    false,
   176  			name:       "cilium-kafka-egress",
   177  		},
   178  		{
   179  			parserType: policy.ParserTypeKafka,
   180  			ingress:    true,
   181  			name:       "cilium-kafka-ingress",
   182  		},
   183  		{
   184  			parserType: policy.ParserTypeDNS,
   185  			ingress:    false,
   186  			name:       "cilium-dns-egress",
   187  		},
   188  		{
   189  			parserType: policy.ParserTypeNone,
   190  			ingress:    false,
   191  			name:       "cilium-proxylib-egress",
   192  		},
   193  		{
   194  			parserType: policy.ParserTypeNone,
   195  			ingress:    true,
   196  			name:       "cilium-proxylib-ingress",
   197  		},
   198  	}
   199  )
   200  
   201  // Called with proxyPortsMutex held!
   202  func isPortAvailable(openLocalPorts map[uint16]struct{}, port uint16) bool {
   203  	if _, used := allocatedPorts[port]; used {
   204  		return false // port already used
   205  	}
   206  	if port == 0 {
   207  		return false // zero port requested
   208  	}
   209  	// Check that the port is not already open
   210  	if _, alreadyOpen := openLocalPorts[port]; alreadyOpen {
   211  		return false // port already open
   212  	}
   213  
   214  	return true
   215  }
   216  
   217  // Called with proxyPortsMutex held!
   218  func allocatePort(port, min, max uint16) (uint16, error) {
   219  	// Get a snapshot of the TCP and UDP ports already open locally.
   220  	openLocalPorts := readOpenLocalPorts(append(procNetTCPFiles, procNetUDPFiles...))
   221  
   222  	if isPortAvailable(openLocalPorts, port) {
   223  		return port, nil
   224  	}
   225  
   226  	// TODO: Maybe not create a large permutation each time?
   227  	for _, r := range portRandomizer.Perm(int(max - min + 1)) {
   228  		resPort := uint16(r) + min
   229  
   230  		if isPortAvailable(openLocalPorts, resPort) {
   231  			return resPort, nil
   232  		}
   233  	}
   234  
   235  	return 0, fmt.Errorf("no available proxy ports")
   236  }
   237  
   238  // Called with proxyPortsMutex held!
   239  func (pp *ProxyPort) reservePort() {
   240  	if !pp.configured {
   241  		allocatedPorts[pp.proxyPort] = struct{}{}
   242  		pp.configured = true
   243  	}
   244  }
   245  
   246  // Called with proxyPortsMutex held!
   247  func findProxyPort(name string) *ProxyPort {
   248  	for i := range proxyPorts {
   249  		if proxyPorts[i].name == name {
   250  			return &proxyPorts[i]
   251  		}
   252  	}
   253  	return nil
   254  }
   255  
   256  // ackProxyPort() marks the proxy as successfully created and creates or updates the datapath rules
   257  // accordingly. Each call must eventually be paired with a corresponding releaseProxyPort() call
   258  // to keep the use count up-to-date.
   259  // Must be called with proxyPortsMutex held!
   260  func (p *Proxy) ackProxyPort(pp *ProxyPort) error {
   261  	if pp.nRedirects == 0 {
   262  		scopedLog := log.WithField("proxy port name", pp.name)
   263  		scopedLog.Debugf("Considering updating proxy port rules for %s:%d (old: %d)", pp.name, pp.proxyPort, pp.rulesPort)
   264  
   265  		// Datapath rules are added only after we know the proxy configuration
   266  		// with the actual port number has succeeded. Deletion of the rules
   267  		// is delayed after the redirects have been removed to the point
   268  		// when we know the port number changes. This is to reduce the churn
   269  		// in the datapath, but means that the datapath rules may exist even
   270  		// if the proxy is not currently configured.
   271  
   272  		// Remove old rules, if any and for different port
   273  		if pp.rulesPort != 0 && pp.rulesPort != pp.proxyPort {
   274  			scopedLog.Infof("Removing old proxy port rules for %s:%d", pp.name, pp.rulesPort)
   275  			p.datapathUpdater.RemoveProxyRules(pp.rulesPort, pp.ingress, pp.name)
   276  			pp.rulesPort = 0
   277  		}
   278  		// Add new rules, if needed
   279  		if pp.rulesPort != pp.proxyPort {
   280  			// This should always succeed if we have managed to start-up properly
   281  			scopedLog.Infof("Adding new proxy port rules for %s:%d", pp.name, pp.proxyPort)
   282  			err := p.datapathUpdater.InstallProxyRules(pp.proxyPort, pp.ingress, pp.name)
   283  			if err != nil {
   284  				return fmt.Errorf("Can't install proxy rules for %s: %s", pp.name, err)
   285  			}
   286  		}
   287  		pp.rulesPort = pp.proxyPort
   288  	}
   289  	pp.nRedirects++
   290  	return nil
   291  }
   292  
   293  // releaseProxyPort() decreases the use count and frees the port if no users remain
   294  // Must be called with proxyPortsMutex held!
   295  func (p *Proxy) releaseProxyPort(name string) error {
   296  	pp := findProxyPort(name)
   297  	if pp == nil {
   298  		return fmt.Errorf("Can't find proxy port %s", name)
   299  	}
   300  	if pp.nRedirects == 0 {
   301  		return fmt.Errorf("Can't release proxy port: proxy %s on %d has refcount 0", name, pp.proxyPort)
   302  	}
   303  
   304  	pp.nRedirects--
   305  	if pp.nRedirects == 0 {
   306  		if pp.isStatic {
   307  			return fmt.Errorf("Can't release proxy port: proxy %s on %d has a static listener", name, pp.proxyPort)
   308  		}
   309  		delete(allocatedPorts, pp.proxyPort)
   310  		// Force new port allocation the next time this ProxyPort is used.
   311  		pp.configured = false
   312  		// Leave the datapath rules behind on the hope that they get reused later.
   313  		// This becomes possible when we are able to keep the proxy listeners
   314  		// configured also when there are no redirects.
   315  		log.WithField(fieldProxyRedirectID, name).Debugf("Delayed release of proxy port %d", pp.proxyPort)
   316  	}
   317  
   318  	return nil
   319  }
   320  
   321  // mutex need not be held, as this only refers to immutable members in the static slice.
   322  func getProxyPort(l7Type policy.L7ParserType, ingress bool) *ProxyPort {
   323  	portType := l7Type
   324  	switch l7Type {
   325  	case policy.ParserTypeDNS, policy.ParserTypeKafka, policy.ParserTypeHTTP:
   326  	default:
   327  		// "Unknown" parsers are assumed to be Proxylib (TCP) parsers, which
   328  		// is registered with an empty string.
   329  		portType = ""
   330  	}
   331  	// proxyPorts is small enough to not bother indexing it.
   332  	for i := range proxyPorts {
   333  		if proxyPorts[i].parserType == portType && proxyPorts[i].ingress == ingress {
   334  			return &proxyPorts[i]
   335  		}
   336  	}
   337  	return nil
   338  }
   339  
   340  func proxyNotFoundError(l7Type policy.L7ParserType, ingress bool) error {
   341  	dir := "egress"
   342  	if ingress {
   343  		dir = "ingress"
   344  	}
   345  	return fmt.Errorf("unrecognized %s proxy type: %s", dir, l7Type)
   346  }
   347  
   348  // Exported API
   349  
   350  // GetProxyPort() returns the fixed listen port for a proxy, if any.
   351  func GetProxyPort(l7Type policy.L7ParserType, ingress bool) (uint16, string, error) {
   352  	// Accessing pp.proxyPort requires the lock
   353  	proxyPortsMutex.Lock()
   354  	defer proxyPortsMutex.Unlock()
   355  	pp := getProxyPort(l7Type, ingress)
   356  	if pp != nil {
   357  		return pp.proxyPort, pp.name, nil
   358  	}
   359  	return 0, "", proxyNotFoundError(l7Type, ingress)
   360  }
   361  
   362  // SetProxyPort() marks the proxy 'name' as successfully created with proxy port 'port' and creates
   363  // or updates the datapath rules accordingly.
   364  // This should only be called for proxies that have a static listener that is already listening on
   365  // 'port'. May only be called once per proxy.
   366  func (p *Proxy) SetProxyPort(name string, port uint16) error {
   367  	proxyPortsMutex.Lock()
   368  	defer proxyPortsMutex.Unlock()
   369  	pp := findProxyPort(name)
   370  	if pp == nil {
   371  		return fmt.Errorf("Can't find proxy port %s", name)
   372  	}
   373  	if pp.nRedirects > 0 {
   374  		return fmt.Errorf("Can't set proxy port to %d: proxy %s is already configured on %d", port, name, pp.proxyPort)
   375  	}
   376  	pp.proxyPort = port
   377  	pp.isStatic = true        // prevents release of the proxy port
   378  	pp.reservePort()          // marks 'port' as reserved, 'pp' as configured
   379  	return p.ackProxyPort(pp) // creates datapath rules, increases the reference count
   380  }
   381  
   382  // ReinstallRules is called by daemon reconfiguration to re-install proxy ports rules that
   383  // were removed during the removal of all Cilium rules.
   384  func (p *Proxy) ReinstallRules() {
   385  	proxyPortsMutex.Lock()
   386  	defer proxyPortsMutex.Unlock()
   387  	for _, pp := range proxyPorts {
   388  		if pp.rulesPort > 0 {
   389  			// This should always succeed if we have managed to start-up properly
   390  			err := p.datapathUpdater.InstallProxyRules(pp.rulesPort, pp.ingress, pp.name)
   391  			if err != nil {
   392  				proxyPortsMutex.Unlock()
   393  				panic(fmt.Sprintf("Can't install proxy rules for %s: %s", pp.name, err))
   394  			}
   395  		}
   396  	}
   397  }
   398  
   399  // CreateOrUpdateRedirect creates or updates a L4 redirect with corresponding
   400  // proxy configuration. This will allocate a proxy port as required and launch
   401  // a proxy instance. If the redirect is already in place, only the rules will be
   402  // updated.
   403  // The proxy listening port is returned, but proxy configuration on that port
   404  // may still be ongoing asynchronously. Caller should wait for successful completion
   405  // on 'wg' before assuming the returned proxy port is listening.
   406  // Caller must call exactly one of the returned functions:
   407  // - finalizeFunc to make the changes stick, or
   408  // - revertFunc to cancel the changes.
   409  func (p *Proxy) CreateOrUpdateRedirect(l4 *policy.L4Filter, id string, localEndpoint logger.EndpointUpdater,
   410  	wg *completion.WaitGroup) (proxyPort uint16, err error, finalizeFunc revert.FinalizeFunc, revertFunc revert.RevertFunc) {
   411  
   412  	p.mutex.Lock()
   413  	defer func() {
   414  		p.updateRedirectMetrics()
   415  		p.mutex.Unlock()
   416  		if err == nil && proxyPort == 0 {
   417  			panic("Trying to configure zero proxy port")
   418  		}
   419  	}()
   420  
   421  	scopedLog := log.WithField(fieldProxyRedirectID, id)
   422  
   423  	var revertStack revert.RevertStack
   424  	revertFunc = revertStack.Revert
   425  
   426  	if redir, ok := p.redirects[id]; ok {
   427  		redir.mutex.Lock()
   428  
   429  		if redir.listener.parserType == l4.L7Parser {
   430  			updateRevertFunc := redir.updateRules(l4)
   431  			revertStack.Push(updateRevertFunc)
   432  			var implUpdateRevertFunc revert.RevertFunc
   433  			implUpdateRevertFunc, err = redir.implementation.UpdateRules(wg, l4)
   434  			if err != nil {
   435  				redir.mutex.Unlock()
   436  				err = fmt.Errorf("unable to update existing redirect: %s", err)
   437  				return 0, err, nil, nil
   438  			}
   439  			revertStack.Push(implUpdateRevertFunc)
   440  
   441  			redir.lastUpdated = time.Now()
   442  
   443  			scopedLog.WithField(logfields.Object, logfields.Repr(redir)).
   444  				Debug("updated existing ", l4.L7Parser, " proxy instance")
   445  
   446  			redir.mutex.Unlock()
   447  
   448  			// Must return the proxy port when successful
   449  			proxyPort = redir.listener.proxyPort
   450  			return
   451  		}
   452  
   453  		var removeRevertFunc revert.RevertFunc
   454  		err, finalizeFunc, removeRevertFunc = p.removeRedirect(id, wg)
   455  		redir.mutex.Unlock()
   456  
   457  		if err != nil {
   458  			err = fmt.Errorf("unable to remove old redirect: %s", err)
   459  			return 0, err, nil, nil
   460  		}
   461  
   462  		revertStack.Push(removeRevertFunc)
   463  	}
   464  
   465  	proxyPortsMutex.Lock()
   466  	defer proxyPortsMutex.Unlock()
   467  	pp := getProxyPort(l4.L7Parser, l4.Ingress)
   468  	if pp == nil {
   469  		err = proxyNotFoundError(l4.L7Parser, l4.Ingress)
   470  		revertFunc()
   471  		return 0, err, nil, nil
   472  	}
   473  
   474  	redir := newRedirect(localEndpoint, pp, uint16(l4.Port))
   475  	redir.updateRules(l4)
   476  	// Rely on create*Redirect to update rules, unlike the update case above.
   477  
   478  	for nRetry := 0; nRetry < redirectCreationAttempts; nRetry++ {
   479  		if nRetry > 0 {
   480  			// an error occurred and we can retry
   481  			scopedLog.WithError(err).Warningf("Unable to create %s proxy, retrying", pp.name)
   482  		}
   483  
   484  		if !pp.configured {
   485  			// Try allocate (the configured) port, but only if the proxy has not
   486  			// been already configured.
   487  			pp.proxyPort, err = allocatePort(pp.proxyPort, p.rangeMin, p.rangeMax)
   488  			if err != nil {
   489  				revertFunc() // Ignore errors while reverting. This is best-effort.
   490  				return 0, err, nil, nil
   491  			}
   492  		}
   493  
   494  		switch l4.L7Parser {
   495  		case policy.ParserTypeDNS:
   496  			redir.implementation, err = createDNSRedirect(redir, dnsConfiguration{}, DefaultEndpointInfoRegistry)
   497  
   498  		case policy.ParserTypeKafka:
   499  			redir.implementation, err = createKafkaRedirect(redir, kafkaConfiguration{}, DefaultEndpointInfoRegistry)
   500  
   501  		case policy.ParserTypeHTTP:
   502  			redir.implementation, err = createEnvoyRedirect(redir, p.stateDir, p.XDSServer, p.datapathUpdater.SupportsOriginalSourceAddr(), wg)
   503  		default:
   504  			redir.implementation, err = createEnvoyRedirect(redir, p.stateDir, p.XDSServer, p.datapathUpdater.SupportsOriginalSourceAddr(), wg)
   505  		}
   506  
   507  		if err == nil {
   508  			scopedLog.WithField(logfields.Object, logfields.Repr(redir)).
   509  				Debug("Created new ", l4.L7Parser, " proxy instance")
   510  			p.redirects[id] = redir
   511  			// must mark the proxyPort configured while we still hold the lock to prevent racing between
   512  			// two parallel runs
   513  			pp.reservePort()
   514  
   515  			revertStack.Push(func() error {
   516  				// Proxy port refcount has not been incremented yet, so it must not be decremented
   517  				// when reverting. Undo what we have done above.
   518  				p.mutex.Lock()
   519  				delete(p.redirects, id)
   520  				p.updateRedirectMetrics()
   521  				p.mutex.Unlock()
   522  				implFinalizeFunc, _ := redir.implementation.Close(wg)
   523  				if implFinalizeFunc != nil {
   524  					implFinalizeFunc()
   525  				}
   526  				return nil
   527  			})
   528  
   529  			// Set the proxy port only after an ACK is received.
   530  			removeFinalizeFunc := finalizeFunc
   531  			finalizeFunc = func() {
   532  				if removeFinalizeFunc != nil {
   533  					removeFinalizeFunc()
   534  				}
   535  
   536  				proxyPortsMutex.Lock()
   537  				err := p.ackProxyPort(pp)
   538  				proxyPortsMutex.Unlock()
   539  				if err != nil {
   540  					// Finalize functions can't error out. This failure can only
   541  					// happen if there is an internal Cilium logic error regarding
   542  					// installation of iptables rules.
   543  					panic(err)
   544  				}
   545  			}
   546  			// Must return the proxy port when successful
   547  			proxyPort = pp.proxyPort
   548  			return
   549  		}
   550  	}
   551  
   552  	// an error occurred, and we have no more retries
   553  	scopedLog.WithError(err).Error("Unable to create ", l4.L7Parser, " proxy")
   554  	revertFunc() // Ignore errors while reverting. This is best-effort.
   555  	return 0, err, nil, nil
   556  }
   557  
   558  // RemoveRedirect removes an existing redirect that has been successfully created earlier.
   559  func (p *Proxy) RemoveRedirect(id string, wg *completion.WaitGroup) (error, revert.FinalizeFunc, revert.RevertFunc) {
   560  	p.mutex.Lock()
   561  	defer func() {
   562  		p.updateRedirectMetrics()
   563  		p.mutex.Unlock()
   564  	}()
   565  	return p.removeRedirect(id, wg)
   566  }
   567  
   568  // removeRedirect removes an existing redirect. p.mutex must be held
   569  // p.mutex must NOT be held when the returned revert function is called!
   570  // proxyPortsMutex must NOT be held when the returned finalize function is called!
   571  func (p *Proxy) removeRedirect(id string, wg *completion.WaitGroup) (err error, finalizeFunc revert.FinalizeFunc, revertFunc revert.RevertFunc) {
   572  	log.WithField(fieldProxyRedirectID, id).
   573  		Debug("Removing proxy redirect")
   574  
   575  	r, ok := p.redirects[id]
   576  	if !ok {
   577  		return fmt.Errorf("unable to find redirect %s", id), nil, nil
   578  	}
   579  	delete(p.redirects, id)
   580  
   581  	implFinalizeFunc, implRevertFunc := r.implementation.Close(wg)
   582  
   583  	// Delay the release and reuse of the port number so it is guaranteed to be
   584  	// safe to listen on the port again. This can't be reverted, so do it in a
   585  	// FinalizeFunc.
   586  	proxyPort := r.listener.proxyPort
   587  	listenerName := r.listener.name
   588  
   589  	finalizeFunc = func() {
   590  		// break GC loop (implementation may point back to 'r')
   591  		r.implementation = nil
   592  
   593  		if implFinalizeFunc != nil {
   594  			implFinalizeFunc()
   595  		}
   596  
   597  		go func() {
   598  			time.Sleep(portReuseDelay)
   599  
   600  			proxyPortsMutex.Lock()
   601  			err := p.releaseProxyPort(listenerName)
   602  			proxyPortsMutex.Unlock()
   603  			if err != nil {
   604  				log.WithField(fieldProxyRedirectID, id).WithError(err).Warningf("Releasing proxy port %d failed", proxyPort)
   605  			}
   606  		}()
   607  	}
   608  
   609  	revertFunc = func() error {
   610  		if implRevertFunc != nil {
   611  			return implRevertFunc()
   612  		}
   613  
   614  		p.mutex.Lock()
   615  		p.redirects[id] = r
   616  		p.mutex.Unlock()
   617  
   618  		return nil
   619  	}
   620  
   621  	return
   622  }
   623  
   624  // ChangeLogLevel changes proxy log level to correspond to the logrus log level 'level'.
   625  func ChangeLogLevel(level logrus.Level) {
   626  	if envoyProxy != nil {
   627  		envoyProxy.ChangeLogLevel(level)
   628  	}
   629  }
   630  
   631  // GetStatusModel returns the proxy status as API model
   632  func (p *Proxy) GetStatusModel() *models.ProxyStatus {
   633  	p.mutex.RLock()
   634  	defer p.mutex.RUnlock()
   635  
   636  	return &models.ProxyStatus{
   637  		IP:        node.GetInternalIPv4().String(),
   638  		PortRange: fmt.Sprintf("%d-%d", p.rangeMin, p.rangeMax),
   639  	}
   640  }
   641  
   642  // updateRedirectMetrics updates the redirect metrics per application protocol
   643  // in Prometheus. Lock needs to be held to call this function.
   644  func (p *Proxy) updateRedirectMetrics() {
   645  	result := map[string]int{}
   646  	for _, redirect := range p.redirects {
   647  		result[string(redirect.listener.parserType)]++
   648  	}
   649  	for proto, count := range result {
   650  		metrics.ProxyRedirects.WithLabelValues(proto).Set(float64(count))
   651  	}
   652  }
   653  
   654  // FML Added in backport
   655  
   656  // UpdateNetworkPolicy must update the redirect configuration of an endpoint in the proxy
   657  func (p *Proxy) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
   658  	return p.XDSServer.UpdateNetworkPolicy(ep, policy, ingressPolicyEnforced, egressPolicyEnforced, wg)
   659  }
   660  
   661  // UseCurrentNetworkPolicy inserts a Completion to the WaitGroup if the current network policy has not yet been acked
   662  func (p *Proxy) UseCurrentNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, wg *completion.WaitGroup) {
   663  	p.XDSServer.UseCurrentNetworkPolicy(ep, policy, wg)
   664  }