istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tools/istio-iptables/pkg/capture/run.go (about)

     1  // Copyright Istio Authors
     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  package capture
    15  
    16  import (
    17  	"fmt"
    18  	"net"
    19  	"net/netip"
    20  	"os"
    21  	"strings"
    22  
    23  	"github.com/vishvananda/netlink"
    24  
    25  	"istio.io/istio/pkg/log"
    26  	"istio.io/istio/tools/istio-iptables/pkg/builder"
    27  	"istio.io/istio/tools/istio-iptables/pkg/config"
    28  	"istio.io/istio/tools/istio-iptables/pkg/constants"
    29  	dep "istio.io/istio/tools/istio-iptables/pkg/dependencies"
    30  	iptableslog "istio.io/istio/tools/istio-iptables/pkg/log"
    31  )
    32  
    33  type Ops int
    34  
    35  const (
    36  	// AppendOps performs append operations of rules
    37  	AppendOps Ops = iota
    38  	// DeleteOps performs delete operations of rules
    39  	DeleteOps
    40  
    41  	// In TPROXY mode, mark the packet from envoy outbound to app by podIP,
    42  	// this is to prevent it being intercepted to envoy inbound listener.
    43  	outboundMark = "1338"
    44  )
    45  
    46  var opsToString = map[Ops]string{
    47  	AppendOps: "-A",
    48  	DeleteOps: "-D",
    49  }
    50  
    51  type IptablesConfigurator struct {
    52  	ruleBuilder *builder.IptablesRuleBuilder
    53  	// TODO(abhide): Fix dep.Dependencies with better interface
    54  	ext dep.Dependencies
    55  	cfg *config.Config
    56  }
    57  
    58  func NewIptablesConfigurator(cfg *config.Config, ext dep.Dependencies) *IptablesConfigurator {
    59  	return &IptablesConfigurator{
    60  		ruleBuilder: builder.NewIptablesRuleBuilder(cfg),
    61  		ext:         ext,
    62  		cfg:         cfg,
    63  	}
    64  }
    65  
    66  type NetworkRange struct {
    67  	IsWildcard    bool
    68  	CIDRs         []netip.Prefix
    69  	HasLoopBackIP bool
    70  }
    71  
    72  func split(s string) []string {
    73  	return config.Split(s)
    74  }
    75  
    76  func (cfg *IptablesConfigurator) separateV4V6(cidrList string) (NetworkRange, NetworkRange, error) {
    77  	if cidrList == "*" {
    78  		return NetworkRange{IsWildcard: true}, NetworkRange{IsWildcard: true}, nil
    79  	}
    80  	ipv6Ranges := NetworkRange{}
    81  	ipv4Ranges := NetworkRange{}
    82  	for _, ipRange := range split(cidrList) {
    83  		ipp, err := netip.ParsePrefix(ipRange)
    84  		if err != nil {
    85  			_, err = fmt.Fprintf(os.Stderr, "Ignoring error for bug compatibility with istio-iptables: %s\n", err.Error())
    86  			if err != nil {
    87  				return ipv4Ranges, ipv6Ranges, err
    88  			}
    89  			continue
    90  		}
    91  		if ipp.Addr().Is4() {
    92  			ipv4Ranges.CIDRs = append(ipv4Ranges.CIDRs, ipp)
    93  			if ipp.Addr().IsLoopback() {
    94  				ipv4Ranges.HasLoopBackIP = true
    95  			}
    96  		} else {
    97  			ipv6Ranges.CIDRs = append(ipv6Ranges.CIDRs, ipp)
    98  			if ipp.Addr().IsLoopback() {
    99  				ipv6Ranges.HasLoopBackIP = true
   100  			}
   101  		}
   102  	}
   103  	return ipv4Ranges, ipv6Ranges, nil
   104  }
   105  
   106  func (cfg *IptablesConfigurator) logConfig() {
   107  	// Dump out our environment for debugging purposes.
   108  	var b strings.Builder
   109  	b.WriteString(fmt.Sprintf("ENVOY_PORT=%s\n", os.Getenv("ENVOY_PORT")))
   110  	b.WriteString(fmt.Sprintf("INBOUND_CAPTURE_PORT=%s\n", os.Getenv("INBOUND_CAPTURE_PORT")))
   111  	b.WriteString(fmt.Sprintf("ISTIO_INBOUND_INTERCEPTION_MODE=%s\n", os.Getenv("ISTIO_INBOUND_INTERCEPTION_MODE")))
   112  	b.WriteString(fmt.Sprintf("ISTIO_INBOUND_TPROXY_ROUTE_TABLE=%s\n", os.Getenv("ISTIO_INBOUND_TPROXY_ROUTE_TABLE")))
   113  	b.WriteString(fmt.Sprintf("ISTIO_INBOUND_PORTS=%s\n", os.Getenv("ISTIO_INBOUND_PORTS")))
   114  	b.WriteString(fmt.Sprintf("ISTIO_OUTBOUND_PORTS=%s\n", os.Getenv("ISTIO_OUTBOUND_PORTS")))
   115  	b.WriteString(fmt.Sprintf("ISTIO_LOCAL_EXCLUDE_PORTS=%s\n", os.Getenv("ISTIO_LOCAL_EXCLUDE_PORTS")))
   116  	b.WriteString(fmt.Sprintf("ISTIO_EXCLUDE_INTERFACES=%s\n", os.Getenv("ISTIO_EXCLUDE_INTERFACES")))
   117  	b.WriteString(fmt.Sprintf("ISTIO_SERVICE_CIDR=%s\n", os.Getenv("ISTIO_SERVICE_CIDR")))
   118  	b.WriteString(fmt.Sprintf("ISTIO_SERVICE_EXCLUDE_CIDR=%s\n", os.Getenv("ISTIO_SERVICE_EXCLUDE_CIDR")))
   119  	b.WriteString(fmt.Sprintf("ISTIO_META_DNS_CAPTURE=%s\n", os.Getenv("ISTIO_META_DNS_CAPTURE")))
   120  	b.WriteString(fmt.Sprintf("INVALID_DROP=%s\n", os.Getenv("INVALID_DROP")))
   121  	log.Infof("Istio iptables environment:\n%s", b.String())
   122  	cfg.cfg.Print()
   123  }
   124  
   125  func (cfg *IptablesConfigurator) handleInboundPortsInclude() {
   126  	// Handling of inbound ports. Traffic will be redirected to Envoy, which will process and forward
   127  	// to the local service. If not set, no inbound port will be intercepted by istio iptablesOrFail.
   128  	var table string
   129  	if cfg.cfg.InboundPortsInclude != "" {
   130  		if cfg.cfg.InboundInterceptionMode == constants.TPROXY {
   131  			// When using TPROXY, create a new chain for routing all inbound traffic to
   132  			// Envoy. Any packet entering this chain gets marked with the ${INBOUND_TPROXY_MARK} mark,
   133  			// so that they get routed to the loopback interface in order to get redirected to Envoy.
   134  			// In the ISTIOINBOUND chain, '-j ISTIODIVERT' reroutes to the loopback
   135  			// interface.
   136  			// Mark all inbound packets.
   137  			cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIODIVERT, constants.MANGLE, "-j", constants.MARK, "--set-mark",
   138  				cfg.cfg.InboundTProxyMark)
   139  			cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIODIVERT, constants.MANGLE, "-j", constants.ACCEPT)
   140  
   141  			// Create a new chain for redirecting inbound traffic to the common Envoy
   142  			// port.
   143  			// In the ISTIOINBOUND chain, '-j RETURN' bypasses Envoy and
   144  			// '-j ISTIOTPROXY' redirects to Envoy.
   145  			cfg.ruleBuilder.AppendVersionedRule(cfg.cfg.HostIPv4LoopbackCidr, "::1/128", iptableslog.UndefinedCommand,
   146  				constants.ISTIOTPROXY, constants.MANGLE, "!", "-d", constants.IPVersionSpecific,
   147  				"-p", constants.TCP, "-j", constants.TPROXY,
   148  				"--tproxy-mark", cfg.cfg.InboundTProxyMark+"/0xffffffff", "--on-port", cfg.cfg.InboundCapturePort)
   149  			table = constants.MANGLE
   150  		} else {
   151  			table = constants.NAT
   152  		}
   153  		cfg.ruleBuilder.AppendRule(iptableslog.JumpInbound, constants.PREROUTING, table, "-p", constants.TCP,
   154  			"-j", constants.ISTIOINBOUND)
   155  
   156  		if cfg.cfg.InboundPortsInclude == "*" {
   157  			// Apply any user-specified port exclusions.
   158  			if cfg.cfg.InboundPortsExclude != "" {
   159  				for _, port := range split(cfg.cfg.InboundPortsExclude) {
   160  					cfg.ruleBuilder.AppendRule(iptableslog.ExcludeInboundPort, constants.ISTIOINBOUND, table, "-p", constants.TCP,
   161  						"--dport", port, "-j", constants.RETURN)
   162  				}
   163  			}
   164  			// Redirect remaining inbound traffic to Envoy.
   165  			if cfg.cfg.InboundInterceptionMode == constants.TPROXY {
   166  				// If an inbound packet belongs to an established socket, route it to the
   167  				// loopback interface.
   168  				cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, "-p", constants.TCP,
   169  					"-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", constants.ISTIODIVERT)
   170  				// Otherwise, it's a new connection. Redirect it using TPROXY.
   171  				cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, "-p", constants.TCP,
   172  					"-j", constants.ISTIOTPROXY)
   173  			} else {
   174  				cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.NAT, "-p", constants.TCP,
   175  					"-j", constants.ISTIOINREDIRECT)
   176  			}
   177  		} else {
   178  			// User has specified a non-empty list of ports to be redirected to Envoy.
   179  			for _, port := range split(cfg.cfg.InboundPortsInclude) {
   180  				if cfg.cfg.InboundInterceptionMode == constants.TPROXY {
   181  					cfg.ruleBuilder.AppendRule(iptableslog.IncludeInboundPort, constants.ISTIOINBOUND, constants.MANGLE, "-p", constants.TCP,
   182  						"--dport", port, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", constants.ISTIODIVERT)
   183  					cfg.ruleBuilder.AppendRule(iptableslog.IncludeInboundPort,
   184  						constants.ISTIOINBOUND, constants.MANGLE, "-p", constants.TCP, "--dport", port, "-j", constants.ISTIOTPROXY)
   185  				} else {
   186  					cfg.ruleBuilder.AppendRule(iptableslog.IncludeInboundPort,
   187  						constants.ISTIOINBOUND, constants.NAT, "-p", constants.TCP, "--dport", port, "-j", constants.ISTIOINREDIRECT)
   188  				}
   189  			}
   190  		}
   191  	}
   192  }
   193  
   194  func (cfg *IptablesConfigurator) handleOutboundIncludeRules(
   195  	rangeInclude NetworkRange,
   196  	appendRule func(command iptableslog.Command, chain string, table string, params ...string) *builder.IptablesRuleBuilder,
   197  	insert func(command iptableslog.Command, chain string, table string, position int, params ...string) *builder.IptablesRuleBuilder,
   198  ) {
   199  	// Apply outbound IP inclusions.
   200  	if rangeInclude.IsWildcard {
   201  		// Wildcard specified. Redirect all remaining outbound traffic to Envoy.
   202  		appendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-j", constants.ISTIOREDIRECT)
   203  		for _, internalInterface := range split(cfg.cfg.KubeVirtInterfaces) {
   204  			insert(iptableslog.KubevirtCommand,
   205  				constants.PREROUTING, constants.NAT, 1, "-i", internalInterface, "-j", constants.ISTIOREDIRECT)
   206  		}
   207  	} else if len(rangeInclude.CIDRs) > 0 {
   208  		// User has specified a non-empty list of cidrs to be redirected to Envoy.
   209  		for _, cidr := range rangeInclude.CIDRs {
   210  			for _, internalInterface := range split(cfg.cfg.KubeVirtInterfaces) {
   211  				insert(iptableslog.KubevirtCommand, constants.PREROUTING, constants.NAT, 1, "-i", internalInterface,
   212  					"-d", cidr.String(), "-j", constants.ISTIOREDIRECT)
   213  			}
   214  			appendRule(iptableslog.UndefinedCommand,
   215  				constants.ISTIOOUTPUT, constants.NAT, "-d", cidr.String(), "-j", constants.ISTIOREDIRECT)
   216  		}
   217  		// All other traffic is not redirected.
   218  		appendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-j", constants.RETURN)
   219  	}
   220  }
   221  
   222  func (cfg *IptablesConfigurator) shortCircuitKubeInternalInterface() {
   223  	for _, internalInterface := range split(cfg.cfg.KubeVirtInterfaces) {
   224  		cfg.ruleBuilder.InsertRule(iptableslog.KubevirtCommand, constants.PREROUTING, constants.NAT, 1, "-i", internalInterface, "-j", constants.RETURN)
   225  	}
   226  }
   227  
   228  func (cfg *IptablesConfigurator) shortCircuitExcludeInterfaces() {
   229  	for _, excludeInterface := range split(cfg.cfg.ExcludeInterfaces) {
   230  		cfg.ruleBuilder.AppendRule(
   231  			iptableslog.ExcludeInterfaceCommand, constants.PREROUTING, constants.NAT, "-i", excludeInterface, "-j", constants.RETURN)
   232  		cfg.ruleBuilder.AppendRule(iptableslog.ExcludeInterfaceCommand, constants.OUTPUT, constants.NAT, "-o", excludeInterface, "-j", constants.RETURN)
   233  	}
   234  	if cfg.cfg.InboundInterceptionMode == constants.TPROXY {
   235  		for _, excludeInterface := range split(cfg.cfg.ExcludeInterfaces) {
   236  
   237  			cfg.ruleBuilder.AppendRule(
   238  				iptableslog.ExcludeInterfaceCommand, constants.PREROUTING, constants.MANGLE, "-i", excludeInterface, "-j", constants.RETURN)
   239  			cfg.ruleBuilder.AppendRule(iptableslog.ExcludeInterfaceCommand, constants.OUTPUT, constants.MANGLE, "-o", excludeInterface, "-j", constants.RETURN)
   240  		}
   241  	}
   242  }
   243  
   244  func ignoreExists(err error) error {
   245  	if err == nil {
   246  		return nil
   247  	}
   248  	if strings.Contains(strings.ToLower(err.Error()), "file exists") {
   249  		return nil
   250  	}
   251  	return err
   252  }
   253  
   254  // configureIPv6Addresses sets up a new IP address on local interface. This is used as the source IP
   255  // for inbound traffic to distinguish traffic we want to capture vs traffic we do not. This is needed
   256  // for IPv6 but not IPv4, as IPv4 defaults to `netmask 255.0.0.0`, which allows binding to addresses
   257  // in the 127.x.y.z range, while IPv6 defaults to `prefixlen 128` which allows binding only to ::1.
   258  // Equivalent to `ip -6 addr add "::6/128" dev lo`
   259  func configureIPv6Addresses(cfg *config.Config) error {
   260  	if !cfg.EnableIPv6 {
   261  		return nil
   262  	}
   263  	link, err := netlink.LinkByName("lo")
   264  	if err != nil {
   265  		return fmt.Errorf("failed to find 'lo' link: %v", err)
   266  	}
   267  	// Setup a new IP address on local interface. This is used as the source IP for inbound traffic
   268  	// to distinguish traffic we want to capture vs traffic we do not.
   269  	// Equivalent to `ip -6 addr add "::6/128" dev lo`
   270  	address := &net.IPNet{IP: net.ParseIP("::6"), Mask: net.CIDRMask(128, 128)}
   271  	addr := &netlink.Addr{IPNet: address}
   272  
   273  	err = netlink.AddrAdd(link, addr)
   274  	if ignoreExists(err) != nil {
   275  		return fmt.Errorf("failed to add IPv6 inbound address: %v", err)
   276  	}
   277  	log.Infof("Added ::6 address")
   278  	return nil
   279  }
   280  
   281  func (cfg *IptablesConfigurator) Run() error {
   282  	iptVer, err := cfg.ext.DetectIptablesVersion(false)
   283  	if err != nil {
   284  		return err
   285  	}
   286  
   287  	ipt6Ver, err := cfg.ext.DetectIptablesVersion(true)
   288  	if err != nil {
   289  		return err
   290  	}
   291  
   292  	defer func() {
   293  		// Best effort since we don't know if the commands exist
   294  		_ = cfg.ext.Run(constants.IPTablesSave, &iptVer, nil)
   295  		if cfg.cfg.EnableIPv6 {
   296  			_ = cfg.ext.Run(constants.IPTablesSave, &ipt6Ver, nil)
   297  		}
   298  	}()
   299  
   300  	// Since OUTBOUND_IP_RANGES_EXCLUDE could carry ipv4 and ipv6 ranges
   301  	// need to split them in different arrays one for ipv4 and one for ipv6
   302  	// in order to not to fail
   303  	ipv4RangesExclude, ipv6RangesExclude, err := cfg.separateV4V6(cfg.cfg.OutboundIPRangesExclude)
   304  	if err != nil {
   305  		return err
   306  	}
   307  	if ipv4RangesExclude.IsWildcard {
   308  		return fmt.Errorf("invalid value for OUTBOUND_IP_RANGES_EXCLUDE")
   309  	}
   310  	// FixMe: Do we need similar check for ipv6RangesExclude as well ??
   311  
   312  	ipv4RangesInclude, ipv6RangesInclude, err := cfg.separateV4V6(cfg.cfg.OutboundIPRangesInclude)
   313  	if err != nil {
   314  		return err
   315  	}
   316  
   317  	redirectDNS := cfg.cfg.RedirectDNS
   318  	cfg.logConfig()
   319  
   320  	cfg.shortCircuitExcludeInterfaces()
   321  
   322  	// Do not capture internal interface.
   323  	cfg.shortCircuitKubeInternalInterface()
   324  
   325  	// Create a rule for invalid drop in PREROUTING chain in mangle table, so the iptables will drop the out of window packets instead of reset connection .
   326  	dropInvalid := cfg.cfg.DropInvalid
   327  	if dropInvalid {
   328  		cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.PREROUTING, constants.MANGLE, "-m", "conntrack", "--ctstate",
   329  			"INVALID", "-j", constants.DROP)
   330  	}
   331  
   332  	// Create a new chain for to hit tunnel port directly. Envoy will be listening on port acting as VPN tunnel.
   333  	cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.NAT, "-p", constants.TCP, "--dport",
   334  		cfg.cfg.InboundTunnelPort, "-j", constants.RETURN)
   335  
   336  	// Create a new chain for redirecting outbound traffic to the common Envoy port.
   337  	// In both chains, '-j RETURN' bypasses Envoy and '-j ISTIOREDIRECT'
   338  	// redirects to Envoy.
   339  	cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand,
   340  		constants.ISTIOREDIRECT, constants.NAT, "-p", constants.TCP, "-j", constants.REDIRECT, "--to-ports", cfg.cfg.ProxyPort)
   341  
   342  	// Use this chain also for redirecting inbound traffic to the common Envoy port
   343  	// when not using TPROXY.
   344  
   345  	cfg.ruleBuilder.AppendRule(iptableslog.InboundCapture, constants.ISTIOINREDIRECT, constants.NAT, "-p", constants.TCP, "-j", constants.REDIRECT,
   346  		"--to-ports", cfg.cfg.InboundCapturePort)
   347  
   348  	cfg.handleInboundPortsInclude()
   349  
   350  	// TODO: change the default behavior to not intercept any output - user may use http_proxy or another
   351  	// iptablesOrFail wrapper (like ufw). Current default is similar with 0.1
   352  	// Jump to the ISTIOOUTPUT chain from OUTPUT chain for all tcp traffic
   353  	cfg.ruleBuilder.AppendRule(iptableslog.JumpOutbound, constants.OUTPUT, constants.NAT, "-p", constants.TCP, "-j", constants.ISTIOOUTPUT)
   354  	// Apply port based exclusions. Must be applied before connections back to self are redirected.
   355  	if cfg.cfg.OutboundPortsExclude != "" {
   356  		for _, port := range split(cfg.cfg.OutboundPortsExclude) {
   357  			cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-p", constants.TCP,
   358  				"--dport", port, "-j", constants.RETURN)
   359  		}
   360  	}
   361  
   362  	// 127.0.0.6/::7 is bind connect from inbound passthrough cluster
   363  	cfg.ruleBuilder.AppendVersionedRule("127.0.0.6/32", "::6/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   364  		"-o", "lo", "-s", constants.IPVersionSpecific, "-j", constants.RETURN)
   365  
   366  	for _, uid := range split(cfg.cfg.ProxyUID) {
   367  		// Redirect app calls back to itself via Envoy when using the service VIP
   368  		// e.g. appN => Envoy (client) => Envoy (server) => appN.
   369  		// nolint: lll
   370  		if redirectDNS {
   371  			// When DNS is enabled, we skip this for port 53. This ensures we do not have:
   372  			// app => istio-agent => Envoy inbound => dns server
   373  			// Instead, we just have:
   374  			// app => istio-agent => dns server
   375  			cfg.ruleBuilder.AppendVersionedRule(cfg.cfg.HostIPv4LoopbackCidr, "::1/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   376  				"-o", "lo",
   377  				"!", "-d", constants.IPVersionSpecific,
   378  				"-p", "tcp",
   379  				"-m", "multiport",
   380  				"!", "--dports", "53,"+cfg.cfg.InboundTunnelPort,
   381  				"-m", "owner", "--uid-owner", uid, "-j", constants.ISTIOINREDIRECT)
   382  		} else {
   383  			cfg.ruleBuilder.AppendVersionedRule(cfg.cfg.HostIPv4LoopbackCidr, "::1/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   384  				"-o", "lo",
   385  				"!", "-d", constants.IPVersionSpecific,
   386  				"-p", "tcp",
   387  				"!", "--dport", cfg.cfg.InboundTunnelPort,
   388  				"-m", "owner", "--uid-owner", uid, "-j", constants.ISTIOINREDIRECT)
   389  		}
   390  		// Do not redirect app calls to back itself via Envoy when using the endpoint address
   391  		// e.g. appN => appN by lo
   392  		// If loopback explicitly set via OutboundIPRangesInclude, then don't return.
   393  		if !ipv4RangesInclude.HasLoopBackIP && !ipv6RangesInclude.HasLoopBackIP {
   394  			if redirectDNS {
   395  				// Users may have a DNS server that is on localhost. In these cases, applications may
   396  				// send TCP traffic to the DNS server that we actually *do* want to intercept. To
   397  				// handle this case, we exclude port 53 from this rule. Note: We cannot just move the
   398  				// port 53 redirection rule further up the list, as we will want to avoid capturing
   399  				// DNS requests from the proxy UID/GID
   400  				cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-o", "lo", "-p", "tcp",
   401  					"!", "--dport", "53",
   402  					"-m", "owner", "!", "--uid-owner", uid, "-j", constants.RETURN)
   403  			} else {
   404  				cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   405  					"-o", "lo", "-m", "owner", "!", "--uid-owner", uid, "-j", constants.RETURN)
   406  			}
   407  		}
   408  
   409  		// Avoid infinite loops. Don't redirect Envoy traffic directly back to
   410  		// Envoy for non-loopback traffic.
   411  		cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   412  			"-m", "owner", "--uid-owner", uid, "-j", constants.RETURN)
   413  	}
   414  
   415  	for _, gid := range split(cfg.cfg.ProxyGID) {
   416  		// Redirect app calls back to itself via Envoy when using the service VIP
   417  		// e.g. appN => Envoy (client) => Envoy (server) => appN.
   418  		cfg.ruleBuilder.AppendVersionedRule(cfg.cfg.HostIPv4LoopbackCidr, "::1/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   419  			"-o", "lo",
   420  			"!", "-d", constants.IPVersionSpecific,
   421  			"-p", "tcp",
   422  			"!", "--dport", cfg.cfg.InboundTunnelPort,
   423  			"-m", "owner", "--gid-owner", gid, "-j", constants.ISTIOINREDIRECT)
   424  
   425  		// Do not redirect app calls to back itself via Envoy when using the endpoint address
   426  		// e.g. appN => appN by lo
   427  		// If loopback explicitly set via OutboundIPRangesInclude, then don't return.
   428  		if !ipv4RangesInclude.HasLoopBackIP && !ipv6RangesInclude.HasLoopBackIP {
   429  			if redirectDNS {
   430  				// Users may have a DNS server that is on localhost. In these cases, applications may
   431  				// send TCP traffic to the DNS server that we actually *do* want to intercept. To
   432  				// handle this case, we exclude port 53 from this rule. Note: We cannot just move the
   433  				// port 53 redirection rule further up the list, as we will want to avoid capturing
   434  				// DNS requests from the proxy UID/GID
   435  				cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   436  					"-o", "lo", "-p", "tcp",
   437  					"!", "--dport", "53",
   438  					"-m", "owner", "!", "--gid-owner", gid, "-j", constants.RETURN)
   439  			} else {
   440  				cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   441  					"-o", "lo", "-m", "owner", "!", "--gid-owner", gid, "-j", constants.RETURN)
   442  			}
   443  		}
   444  
   445  		// Avoid infinite loops. Don't redirect Envoy traffic directly back to
   446  		// Envoy for non-loopback traffic.
   447  		cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-m", "owner", "--gid-owner", gid, "-j", constants.RETURN)
   448  	}
   449  
   450  	ownerGroupsFilter := config.ParseInterceptFilter(cfg.cfg.OwnerGroupsInclude, cfg.cfg.OwnerGroupsExclude)
   451  
   452  	cfg.handleCaptureByOwnerGroup(ownerGroupsFilter)
   453  
   454  	if redirectDNS {
   455  		if cfg.cfg.CaptureAllDNS {
   456  			// Redirect all TCP dns traffic on port 53 to the agent on port 15053
   457  			// This will be useful for the CNI case where pod DNS server address cannot be decided.
   458  			cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand,
   459  				constants.ISTIOOUTPUT, constants.NAT,
   460  				"-p", constants.TCP,
   461  				"--dport", "53",
   462  				"-j", constants.REDIRECT,
   463  				"--to-ports", constants.IstioAgentDNSListenerPort)
   464  		} else {
   465  			for _, s := range cfg.cfg.DNSServersV4 {
   466  				// redirect all TCP dns traffic on port 53 to the agent on port 15053 for all servers
   467  				// in etc/resolv.conf
   468  				// We avoid redirecting all IP ranges to avoid infinite loops when there are local DNS proxies
   469  				// such as: app -> istio dns server -> dnsmasq -> upstream
   470  				// This ensures that we do not get requests from dnsmasq sent back to the agent dns server in a loop.
   471  				// Note: If a user somehow configured etc/resolv.conf to point to dnsmasq and server X, and dnsmasq also
   472  				// pointed to server X, this would not work. However, the assumption is that is not a common case.
   473  				cfg.ruleBuilder.AppendRuleV4(iptableslog.UndefinedCommand,
   474  					constants.ISTIOOUTPUT, constants.NAT,
   475  					"-p", constants.TCP,
   476  					"--dport", "53",
   477  					"-d", s+"/32",
   478  					"-j", constants.REDIRECT,
   479  					"--to-ports", constants.IstioAgentDNSListenerPort)
   480  			}
   481  			for _, s := range cfg.cfg.DNSServersV6 {
   482  				cfg.ruleBuilder.AppendRuleV6(iptableslog.UndefinedCommand,
   483  					constants.ISTIOOUTPUT, constants.NAT,
   484  					"-p", constants.TCP,
   485  					"--dport", "53",
   486  					"-d", s+"/128",
   487  					"-j", constants.REDIRECT,
   488  					"--to-ports", constants.IstioAgentDNSListenerPort)
   489  			}
   490  		}
   491  	}
   492  
   493  	// Skip redirection for Envoy-aware applications and
   494  	// container-to-container traffic both of which explicitly use
   495  	// localhost.
   496  	cfg.ruleBuilder.AppendVersionedRule(cfg.cfg.HostIPv4LoopbackCidr, "::1/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   497  		"-d", constants.IPVersionSpecific, "-j", constants.RETURN)
   498  	// Apply outbound IPv4 exclusions. Must be applied before inclusions.
   499  	for _, cidr := range ipv4RangesExclude.CIDRs {
   500  		cfg.ruleBuilder.AppendRuleV4(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-d", cidr.String(), "-j", constants.RETURN)
   501  	}
   502  	for _, cidr := range ipv6RangesExclude.CIDRs {
   503  		cfg.ruleBuilder.AppendRuleV6(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-d", cidr.String(), "-j", constants.RETURN)
   504  	}
   505  
   506  	cfg.handleOutboundPortsInclude()
   507  
   508  	cfg.handleOutboundIncludeRules(ipv4RangesInclude, cfg.ruleBuilder.AppendRuleV4, cfg.ruleBuilder.InsertRuleV4)
   509  	cfg.handleOutboundIncludeRules(ipv6RangesInclude, cfg.ruleBuilder.AppendRuleV6, cfg.ruleBuilder.InsertRuleV6)
   510  
   511  	if redirectDNS {
   512  		// Jump from OUTPUT chain to ISTIOOUTPUT chain for all UDP traffic
   513  		cfg.ruleBuilder.AppendRule(iptableslog.JumpOutbound, constants.OUTPUT, constants.NAT, "-p", constants.UDP, "-j", constants.ISTIOOUTPUT)
   514  		cfg.ruleBuilder.AppendRule(iptableslog.JumpOutbound, constants.OUTPUT, constants.RAW, "-p", constants.UDP, "-j", constants.ISTIOOUTPUT)
   515  
   516  		HandleDNSUDP(
   517  			AppendOps, cfg.ruleBuilder, cfg.ext, &iptVer, &ipt6Ver,
   518  			cfg.cfg.ProxyUID, cfg.cfg.ProxyGID,
   519  			cfg.cfg.DNSServersV4, cfg.cfg.DNSServersV6, cfg.cfg.CaptureAllDNS,
   520  			ownerGroupsFilter)
   521  	}
   522  
   523  	if cfg.cfg.InboundInterceptionMode == constants.TPROXY {
   524  		// save packet mark set by envoy.filters.listener.original_src as connection mark
   525  		cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.PREROUTING, constants.MANGLE,
   526  			"-p", constants.TCP, "-m", "mark", "--mark", cfg.cfg.InboundTProxyMark, "-j", "CONNMARK", "--save-mark")
   527  		// If the packet is already marked with 1337, then return. This is to prevent mark envoy --> app traffic again.
   528  		cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.OUTPUT, constants.MANGLE,
   529  			"-p", constants.TCP, "-o", "lo", "-m", "mark", "--mark", cfg.cfg.InboundTProxyMark, "-j", constants.RETURN)
   530  		for _, uid := range split(cfg.cfg.ProxyUID) {
   531  			// mark outgoing packets from envoy to workload by pod ip
   532  			// app call VIP --> envoy outbound -(mark 1338)-> envoy inbound --> app
   533  			cfg.ruleBuilder.AppendVersionedRule(cfg.cfg.HostIPv4LoopbackCidr, "::1/128", iptableslog.UndefinedCommand, constants.OUTPUT, constants.MANGLE,
   534  				"!", "-d", constants.IPVersionSpecific, "-p", constants.TCP, "-o", "lo",
   535  				"-m", "owner", "--uid-owner", uid, "-j", constants.MARK, "--set-mark", outboundMark)
   536  		}
   537  		for _, gid := range split(cfg.cfg.ProxyGID) {
   538  			// mark outgoing packets from envoy to workload by pod ip
   539  			// app call VIP --> envoy outbound -(mark 1338)-> envoy inbound --> app
   540  			cfg.ruleBuilder.AppendVersionedRule(cfg.cfg.HostIPv4LoopbackCidr, "::1/128", iptableslog.UndefinedCommand, constants.OUTPUT, constants.MANGLE,
   541  				"!", "-d", constants.IPVersionSpecific, "-p", constants.TCP, "-o", "lo",
   542  				"-m", "owner", "--gid-owner", gid, "-j", constants.MARK, "--set-mark", outboundMark)
   543  		}
   544  		// mark outgoing packets from workload, match it to policy routing entry setup for TPROXY mode
   545  		cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.OUTPUT, constants.MANGLE,
   546  			"-p", constants.TCP, "-m", "connmark", "--mark", cfg.cfg.InboundTProxyMark, "-j", "CONNMARK", "--restore-mark")
   547  		// prevent infinite redirect
   548  		cfg.ruleBuilder.InsertRule(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, 1,
   549  			"-p", constants.TCP, "-m", "mark", "--mark", cfg.cfg.InboundTProxyMark, "-j", constants.RETURN)
   550  		// prevent intercept traffic from envoy/pilot-agent ==> app by 127.0.0.6 --> podip
   551  		cfg.ruleBuilder.InsertRuleV4(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, 2,
   552  			"-p", constants.TCP, "-s", "127.0.0.6/32", "-i", "lo", "-j", constants.RETURN)
   553  		cfg.ruleBuilder.InsertRuleV6(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, 2,
   554  			"-p", constants.TCP, "-s", "::6/128", "-i", "lo", "-j", constants.RETURN)
   555  		// prevent intercept traffic from app ==> app by pod ip
   556  		cfg.ruleBuilder.InsertRule(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, 3,
   557  			"-p", constants.TCP, "-i", "lo", "-m", "mark", "!", "--mark", outboundMark, "-j", constants.RETURN)
   558  	}
   559  	return cfg.executeCommands(&iptVer, &ipt6Ver)
   560  }
   561  
   562  type UDPRuleApplier struct {
   563  	iptables *builder.IptablesRuleBuilder
   564  	ext      dep.Dependencies
   565  	ops      Ops
   566  	table    string
   567  	chain    string
   568  	iptV     *dep.IptablesVersion
   569  	ipt6V    *dep.IptablesVersion
   570  }
   571  
   572  func (f UDPRuleApplier) RunV4(args ...string) {
   573  	switch f.ops {
   574  	case AppendOps:
   575  		f.iptables.AppendRuleV4(iptableslog.UndefinedCommand, f.chain, f.table, args...)
   576  	case DeleteOps:
   577  		deleteArgs := []string{"-t", f.table, opsToString[f.ops], f.chain}
   578  		deleteArgs = append(deleteArgs, args...)
   579  		f.ext.RunQuietlyAndIgnore(constants.IPTables, f.iptV, nil, deleteArgs...)
   580  	}
   581  }
   582  
   583  func (f UDPRuleApplier) RunV6(args ...string) {
   584  	switch f.ops {
   585  	case AppendOps:
   586  		f.iptables.AppendRuleV6(iptableslog.UndefinedCommand, f.chain, f.table, args...)
   587  	case DeleteOps:
   588  		deleteArgs := []string{"-t", f.table, opsToString[f.ops], f.chain}
   589  		deleteArgs = append(deleteArgs, args...)
   590  		f.ext.RunQuietlyAndIgnore(constants.IPTables, f.ipt6V, nil, deleteArgs...)
   591  	}
   592  }
   593  
   594  func (f UDPRuleApplier) Run(args ...string) {
   595  	f.RunV4(args...)
   596  	f.RunV6(args...)
   597  }
   598  
   599  func (f UDPRuleApplier) WithChain(chain string) UDPRuleApplier {
   600  	f.chain = chain
   601  	return f
   602  }
   603  
   604  func (f UDPRuleApplier) WithTable(table string) UDPRuleApplier {
   605  	f.table = table
   606  	return f
   607  }
   608  
   609  // HandleDNSUDP is a helper function to tackle with DNS UDP specific operations.
   610  // This helps the creation logic of DNS UDP rules in sync with the deletion.
   611  func HandleDNSUDP(
   612  	ops Ops, iptables *builder.IptablesRuleBuilder, ext dep.Dependencies,
   613  	iptV, ipt6V *dep.IptablesVersion, proxyUID, proxyGID string, dnsServersV4 []string, dnsServersV6 []string, captureAllDNS bool,
   614  	ownerGroupsFilter config.InterceptFilter,
   615  ) {
   616  	// TODO BML drop "UDPRuleApplier", it is a largely useless type.
   617  	// we do not need a unique type just to apply UDP iptables rules
   618  	f := UDPRuleApplier{
   619  		iptables: iptables,
   620  		ext:      ext,
   621  		ops:      ops,
   622  		table:    constants.NAT,
   623  		chain:    constants.ISTIOOUTPUT,
   624  		iptV:     iptV,
   625  		ipt6V:    ipt6V,
   626  	}
   627  	// Make sure that upstream DNS requests from agent/envoy dont get captured.
   628  	// TODO: add ip6 as well
   629  	for _, uid := range split(proxyUID) {
   630  		f.Run("-p", "udp", "--dport", "53", "-m", "owner", "--uid-owner", uid, "-j", constants.RETURN)
   631  	}
   632  	for _, gid := range split(proxyGID) {
   633  		f.Run("-p", "udp", "--dport", "53", "-m", "owner", "--gid-owner", gid, "-j", constants.RETURN)
   634  	}
   635  
   636  	if ownerGroupsFilter.Except {
   637  		for _, group := range ownerGroupsFilter.Values {
   638  			f.Run("-p", "udp", "--dport", "53", "-m", "owner", "--gid-owner", group, "-j", constants.RETURN)
   639  		}
   640  	} else {
   641  		groupIsNoneOf := CombineMatchers(ownerGroupsFilter.Values, func(group string) []string {
   642  			return []string{"-m", "owner", "!", "--gid-owner", group}
   643  		})
   644  		f.Run(Flatten([]string{"-p", "udp", "--dport", "53"}, groupIsNoneOf, []string{"-j", constants.RETURN})...)
   645  	}
   646  
   647  	if captureAllDNS {
   648  		// Redirect all UDP dns traffic on port 53 to the agent on port 15053
   649  		// This will be useful for the CNI case where pod DNS server address cannot be decided.
   650  		f.Run("-p", "udp", "--dport", "53", "-j", constants.REDIRECT, "--to-port", constants.IstioAgentDNSListenerPort)
   651  	} else {
   652  		// redirect all UDP dns traffic on port 53 to the agent on port 15053 for all servers
   653  		// in etc/resolv.conf
   654  		// We avoid redirecting all IP ranges to avoid infinite loops when there are local DNS proxies
   655  		// such as: app -> istio dns server -> dnsmasq -> upstream
   656  		// This ensures that we do not get requests from dnsmasq sent back to the agent dns server in a loop.
   657  		// Note: If a user somehow configured etc/resolv.conf to point to dnsmasq and server X, and dnsmasq also
   658  		// pointed to server X, this would not work. However, the assumption is that is not a common case.
   659  		for _, s := range dnsServersV4 {
   660  			f.RunV4("-p", "udp", "--dport", "53", "-d", s+"/32",
   661  				"-j", constants.REDIRECT, "--to-port", constants.IstioAgentDNSListenerPort)
   662  		}
   663  		for _, s := range dnsServersV6 {
   664  			f.RunV6("-p", "udp", "--dport", "53", "-d", s+"/128",
   665  				"-j", constants.REDIRECT, "--to-port", constants.IstioAgentDNSListenerPort)
   666  		}
   667  	}
   668  	// Split UDP DNS traffic to separate conntrack zones
   669  	addConntrackZoneDNSUDP(f.WithTable(constants.RAW), proxyUID, proxyGID, dnsServersV4, dnsServersV6, captureAllDNS)
   670  }
   671  
   672  // addConntrackZoneDNSUDP is a helper function to add iptables rules to split DNS traffic
   673  // in two separate conntrack zones to avoid issues with UDP conntrack race conditions.
   674  // Traffic that goes from istio to DNS servers and vice versa are zone 1 and traffic from
   675  // DNS client to istio and vice versa goes to zone 2
   676  func addConntrackZoneDNSUDP(
   677  	f UDPRuleApplier, proxyUID, proxyGID string, dnsServersV4 []string, dnsServersV6 []string, captureAllDNS bool,
   678  ) {
   679  	// TODO: add ip6 as well
   680  	for _, uid := range split(proxyUID) {
   681  		// Packets with dst port 53 from istio to zone 1. These are Istio calls to upstream resolvers
   682  		f.Run("-p", "udp", "--dport", "53", "-m", "owner", "--uid-owner", uid, "-j", constants.CT, "--zone", "1")
   683  		// Packets with src port 15053 from istio to zone 2. These are Istio response packets to application clients
   684  		f.Run("-p", "udp", "--sport", "15053", "-m", "owner", "--uid-owner", uid, "-j", constants.CT, "--zone", "2")
   685  	}
   686  	for _, gid := range split(proxyGID) {
   687  		// Packets with dst port 53 from istio to zone 1. These are Istio calls to upstream resolvers
   688  		f.Run("-p", "udp", "--dport", "53", "-m", "owner", "--gid-owner", gid, "-j", constants.CT, "--zone", "1")
   689  		// Packets with src port 15053 from istio to zone 2. These are Istio response packets to application clients
   690  		f.Run("-p", "udp", "--sport", "15053", "-m", "owner", "--gid-owner", gid, "-j", constants.CT, "--zone", "2")
   691  
   692  	}
   693  
   694  	if captureAllDNS {
   695  		// Not specifying destination address is useful for the CNI case where pod DNS server address cannot be decided.
   696  
   697  		// Mark all UDP dns traffic with dst port 53 as zone 2. These are application client packets towards DNS resolvers.
   698  		f.Run("-p", "udp", "--dport", "53",
   699  			"-j", constants.CT, "--zone", "2")
   700  		// Mark all UDP dns traffic with src port 53 as zone 1. These are response packets from the DNS resolvers.
   701  		f.WithChain(constants.PREROUTING).Run("-p", "udp", "--sport", "53",
   702  			"-j", constants.CT, "--zone", "1")
   703  	} else {
   704  		// Go through all DNS servers in etc/resolv.conf and mark the packets based on these destination addresses.
   705  		for _, s := range dnsServersV4 {
   706  			// Mark all UDP dns traffic with dst port 53 as zone 2. These are application client packets towards DNS resolvers.
   707  			f.RunV4("-p", "udp", "--dport", "53", "-d", s+"/32",
   708  				"-j", constants.CT, "--zone", "2")
   709  			// Mark all UDP dns traffic with src port 53 as zone 1. These are response packets from the DNS resolvers.
   710  			f.WithChain(constants.PREROUTING).RunV4("-p", "udp", "--sport", "53", "-s", s+"/32",
   711  				"-j", constants.CT, "--zone", "1")
   712  		}
   713  		for _, s := range dnsServersV6 {
   714  			// Mark all UDP dns traffic with dst port 53 as zone 2. These are application client packets towards DNS resolvers.
   715  			f.RunV6("-p", "udp", "--dport", "53", "-d", s+"/128",
   716  				"-j", constants.CT, "--zone", "2")
   717  			// Mark all UDP dns traffic with src port 53 as zone 1. These are response packets from the DNS resolvers.
   718  			f.WithChain(constants.PREROUTING).RunV6("-p", "udp", "--sport", "53", "-s", s+"/128",
   719  				"-j", constants.CT, "--zone", "1")
   720  		}
   721  	}
   722  }
   723  
   724  func (cfg *IptablesConfigurator) handleOutboundPortsInclude() {
   725  	if cfg.cfg.OutboundPortsInclude != "" {
   726  		for _, port := range split(cfg.cfg.OutboundPortsInclude) {
   727  			cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand,
   728  				constants.ISTIOOUTPUT, constants.NAT, "-p", constants.TCP, "--dport", port, "-j", constants.ISTIOREDIRECT)
   729  		}
   730  	}
   731  }
   732  
   733  func (cfg *IptablesConfigurator) handleCaptureByOwnerGroup(filter config.InterceptFilter) {
   734  	if filter.Except {
   735  		for _, group := range filter.Values {
   736  			cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   737  				"-m", "owner", "--gid-owner", group, "-j", constants.RETURN)
   738  		}
   739  	} else {
   740  		groupIsNoneOf := CombineMatchers(filter.Values, func(group string) []string {
   741  			return []string{"-m", "owner", "!", "--gid-owner", group}
   742  		})
   743  		cfg.ruleBuilder.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
   744  			append(groupIsNoneOf, "-j", constants.RETURN)...)
   745  	}
   746  }
   747  
   748  func (cfg *IptablesConfigurator) executeIptablesCommands(iptVer *dep.IptablesVersion, commands [][]string) error {
   749  	for _, cmd := range commands {
   750  		if err := cfg.ext.Run(constants.IPTables, iptVer, nil, cmd...); err != nil {
   751  			return err
   752  		}
   753  	}
   754  	return nil
   755  }
   756  
   757  func (cfg *IptablesConfigurator) executeIptablesRestoreCommand(iptVer *dep.IptablesVersion, isIpv4 bool) error {
   758  	var data string
   759  	if isIpv4 {
   760  		data = cfg.ruleBuilder.BuildV4Restore()
   761  	} else {
   762  		data = cfg.ruleBuilder.BuildV6Restore()
   763  	}
   764  
   765  	log.Infof("Running iptables restore with: %s and the following input:\n%v", iptVer.CmdToString(constants.IPTablesRestore), strings.TrimSpace(data))
   766  	// --noflush to prevent flushing/deleting previous contents from table
   767  	return cfg.ext.Run(constants.IPTablesRestore, iptVer, strings.NewReader(data), "--noflush")
   768  }
   769  
   770  func (cfg *IptablesConfigurator) executeCommands(iptVer, ipt6Ver *dep.IptablesVersion) error {
   771  	if cfg.cfg.RestoreFormat {
   772  		// Execute iptables-restore
   773  		if err := cfg.executeIptablesRestoreCommand(iptVer, true); err != nil {
   774  			return err
   775  		}
   776  		// Execute ip6tables-restore
   777  		if err := cfg.executeIptablesRestoreCommand(ipt6Ver, false); err != nil {
   778  			return err
   779  		}
   780  	} else {
   781  		// Execute iptables commands
   782  		if err := cfg.executeIptablesCommands(iptVer, cfg.ruleBuilder.BuildV4()); err != nil {
   783  			return err
   784  		}
   785  		// Execute ip6tables commands
   786  		if err := cfg.executeIptablesCommands(ipt6Ver, cfg.ruleBuilder.BuildV6()); err != nil {
   787  			return err
   788  		}
   789  	}
   790  	return nil
   791  }