github.com/elfadel/cilium@v1.6.12/pkg/datapath/linux/config.go (about)

     1  // Copyright 2019 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 linux
    16  
    17  import (
    18  	"bufio"
    19  	"encoding/base64"
    20  	"encoding/json"
    21  	"fmt"
    22  	"io"
    23  	"reflect"
    24  	"sort"
    25  
    26  	"github.com/cilium/cilium/pkg/bpf"
    27  	"github.com/cilium/cilium/pkg/byteorder"
    28  	"github.com/cilium/cilium/pkg/datapath"
    29  	"github.com/cilium/cilium/pkg/datapath/iptables"
    30  	"github.com/cilium/cilium/pkg/defaults"
    31  	"github.com/cilium/cilium/pkg/identity"
    32  	"github.com/cilium/cilium/pkg/labels"
    33  	bpfconfig "github.com/cilium/cilium/pkg/maps/configmap"
    34  	"github.com/cilium/cilium/pkg/maps/ctmap"
    35  	"github.com/cilium/cilium/pkg/maps/encrypt"
    36  	"github.com/cilium/cilium/pkg/maps/eppolicymap"
    37  	"github.com/cilium/cilium/pkg/maps/ipcache"
    38  	ipcachemap "github.com/cilium/cilium/pkg/maps/ipcache"
    39  	"github.com/cilium/cilium/pkg/maps/lbmap"
    40  	"github.com/cilium/cilium/pkg/maps/lxcmap"
    41  	"github.com/cilium/cilium/pkg/maps/metricsmap"
    42  	"github.com/cilium/cilium/pkg/maps/nat"
    43  	"github.com/cilium/cilium/pkg/maps/policymap"
    44  	"github.com/cilium/cilium/pkg/maps/sockmap"
    45  	"github.com/cilium/cilium/pkg/maps/tunnel"
    46  	"github.com/cilium/cilium/pkg/node"
    47  	"github.com/cilium/cilium/pkg/option"
    48  	"github.com/cilium/cilium/pkg/signal"
    49  
    50  	"github.com/vishvananda/netlink"
    51  )
    52  
    53  func writeIncludes(w io.Writer) (int, error) {
    54  	return fmt.Fprintf(w, "#include \"lib/utils.h\"\n\n")
    55  }
    56  
    57  // WriteNodeConfig writes the local node configuration to the specified writer.
    58  func (l *linuxDatapath) WriteNodeConfig(w io.Writer, cfg *datapath.LocalNodeConfiguration) error {
    59  	extraMacrosMap := make(map[string]string)
    60  	cDefinesMap := make(map[string]string)
    61  
    62  	fw := bufio.NewWriter(w)
    63  
    64  	writeIncludes(w)
    65  
    66  	routerIP := node.GetIPv6Router()
    67  	hostIP := node.GetIPv6()
    68  
    69  	fmt.Fprintf(fw, "/*\n")
    70  	fmt.Fprintf(fw, " cilium.v6.external.str %s\n", node.GetIPv6().String())
    71  	fmt.Fprintf(fw, " cilium.v6.internal.str %s\n", node.GetIPv6Router().String())
    72  	fmt.Fprintf(fw, " cilium.v6.nodeport.str %s\n", node.GetNodePortIPv6().String())
    73  	fmt.Fprintf(fw, "\n")
    74  	fmt.Fprintf(fw, " cilium.v4.external.str %s\n", node.GetExternalIPv4().String())
    75  	fmt.Fprintf(fw, " cilium.v4.internal.str %s\n", node.GetInternalIPv4().String())
    76  	fmt.Fprintf(fw, " cilium.v4.nodeport.str %s\n", node.GetNodePortIPv4().String())
    77  	fmt.Fprintf(fw, "\n")
    78  	fw.WriteString(dumpRaw(defaults.RestoreV6Addr, node.GetIPv6Router()))
    79  	fw.WriteString(dumpRaw(defaults.RestoreV4Addr, node.GetInternalIPv4()))
    80  	fmt.Fprintf(fw, " */\n\n")
    81  
    82  	if option.Config.EnableIPv6 {
    83  		extraMacrosMap["ROUTER_IP"] = routerIP.String()
    84  		fw.WriteString(defineIPv6("ROUTER_IP", routerIP))
    85  		if option.Config.EnableNodePort {
    86  			ipv6NP := node.GetNodePortIPv6()
    87  			extraMacrosMap["IPV6_NODEPORT"] = ipv6NP.String()
    88  			fw.WriteString(defineIPv6("IPV6_NODEPORT", ipv6NP))
    89  		}
    90  	}
    91  
    92  	if option.Config.EnableIPv4 {
    93  		ipv4GW := node.GetInternalIPv4()
    94  		loopbackIPv4 := node.GetIPv4Loopback()
    95  		ipv4Range := node.GetIPv4AllocRange()
    96  		cDefinesMap["IPV4_GATEWAY"] = fmt.Sprintf("%#x", byteorder.HostSliceToNetwork(ipv4GW, reflect.Uint32).(uint32))
    97  		cDefinesMap["IPV4_LOOPBACK"] = fmt.Sprintf("%#x", byteorder.HostSliceToNetwork(loopbackIPv4, reflect.Uint32).(uint32))
    98  		cDefinesMap["IPV4_MASK"] = fmt.Sprintf("%#x", byteorder.HostSliceToNetwork(ipv4Range.Mask, reflect.Uint32).(uint32))
    99  
   100  		if option.Config.EnableNodePort {
   101  			ipv4NP := node.GetNodePortIPv4()
   102  			cDefinesMap["IPV4_NODEPORT"] = fmt.Sprintf("%#x", byteorder.HostSliceToNetwork(ipv4NP, reflect.Uint32).(uint32))
   103  		}
   104  	}
   105  
   106  	if nat46Range := option.Config.NAT46Prefix; nat46Range != nil {
   107  		fw.WriteString(FmtDefineAddress("NAT46_PREFIX", nat46Range.IP))
   108  	}
   109  
   110  	extraMacrosMap["HOST_IP"] = hostIP.String()
   111  	fw.WriteString(defineIPv6("HOST_IP", hostIP))
   112  
   113  	cDefinesMap["HOST_ID"] = fmt.Sprintf("%d", identity.GetReservedID(labels.IDNameHost))
   114  	cDefinesMap["WORLD_ID"] = fmt.Sprintf("%d", identity.GetReservedID(labels.IDNameWorld))
   115  	cDefinesMap["HEALTH_ID"] = fmt.Sprintf("%d", identity.GetReservedID(labels.IDNameHealth))
   116  	cDefinesMap["UNMANAGED_ID"] = fmt.Sprintf("%d", identity.GetReservedID(labels.IDNameUnmanaged))
   117  	cDefinesMap["INIT_ID"] = fmt.Sprintf("%d", identity.GetReservedID(labels.IDNameInit))
   118  	cDefinesMap["LB_RR_MAX_SEQ"] = fmt.Sprintf("%d", lbmap.MaxSeq)
   119  	cDefinesMap["CILIUM_LB_MAP_MAX_ENTRIES"] = fmt.Sprintf("%d", lbmap.MaxEntries)
   120  	cDefinesMap["TUNNEL_MAP"] = tunnel.MapName
   121  	cDefinesMap["TUNNEL_ENDPOINT_MAP_SIZE"] = fmt.Sprintf("%d", tunnel.MaxEntries)
   122  	cDefinesMap["ENDPOINTS_MAP"] = lxcmap.MapName
   123  	cDefinesMap["ENDPOINTS_MAP_SIZE"] = fmt.Sprintf("%d", lxcmap.MaxEntries)
   124  	cDefinesMap["METRICS_MAP"] = metricsmap.MapName
   125  	cDefinesMap["METRICS_MAP_SIZE"] = fmt.Sprintf("%d", metricsmap.MaxEntries)
   126  	cDefinesMap["POLICY_MAP_SIZE"] = fmt.Sprintf("%d", policymap.MaxEntries)
   127  	cDefinesMap["IPCACHE_MAP"] = ipcachemap.Name
   128  	cDefinesMap["IPCACHE_MAP_SIZE"] = fmt.Sprintf("%d", ipcachemap.MaxEntries)
   129  	cDefinesMap["POLICY_PROG_MAP_SIZE"] = fmt.Sprintf("%d", policymap.ProgArrayMaxEntries)
   130  	cDefinesMap["SOCKOPS_MAP_SIZE"] = fmt.Sprintf("%d", sockmap.MaxEntries)
   131  	cDefinesMap["ENCRYPT_MAP"] = encrypt.MapName
   132  	cDefinesMap["CT_CONNECTION_LIFETIME_TCP"] = fmt.Sprintf("%d", int64(option.Config.CTMapEntriesTimeoutTCP.Seconds()))
   133  	cDefinesMap["CT_CONNECTION_LIFETIME_NONTCP"] = fmt.Sprintf("%d", int64(option.Config.CTMapEntriesTimeoutAny.Seconds()))
   134  	cDefinesMap["CT_SERVICE_LIFETIME_TCP"] = fmt.Sprintf("%d", int64(option.Config.CTMapEntriesTimeoutSVCTCP.Seconds()))
   135  	cDefinesMap["CT_SERVICE_LIFETIME_NONTCP"] = fmt.Sprintf("%d", int64(option.Config.CTMapEntriesTimeoutSVCAny.Seconds()))
   136  	cDefinesMap["CT_SYN_TIMEOUT"] = fmt.Sprintf("%d", int64(option.Config.CTMapEntriesTimeoutSYN.Seconds()))
   137  	cDefinesMap["CT_CLOSE_TIMEOUT"] = fmt.Sprintf("%d", int64(option.Config.CTMapEntriesTimeoutFIN.Seconds()))
   138  
   139  	if option.Config.DatapathMode == option.DatapathModeIpvlan {
   140  		cDefinesMap["ENABLE_SECCTX_FROM_IPCACHE"] = "1"
   141  	}
   142  
   143  	if option.Config.PreAllocateMaps {
   144  		cDefinesMap["PREALLOCATE_MAPS"] = "1"
   145  	}
   146  
   147  	cDefinesMap["EVENTS_MAP"] = "cilium_events"
   148  	cDefinesMap["SIGNAL_MAP"] = signal.SignalMapName
   149  	cDefinesMap["POLICY_CALL_MAP"] = policymap.CallMapName
   150  	cDefinesMap["EP_POLICY_MAP"] = eppolicymap.MapName
   151  	cDefinesMap["LB6_REVERSE_NAT_MAP"] = "cilium_lb6_reverse_nat"
   152  	cDefinesMap["LB6_SERVICES_MAP_V2"] = "cilium_lb6_services_v2"
   153  	cDefinesMap["LB6_BACKEND_MAP"] = "cilium_lb6_backends"
   154  	cDefinesMap["LB6_RR_SEQ_MAP_V2"] = "cilium_lb6_rr_seq_v2"
   155  	cDefinesMap["LB6_REVERSE_NAT_SK_MAP"] = "cilium_lb6_reverse_sk"
   156  	cDefinesMap["LB4_REVERSE_NAT_MAP"] = "cilium_lb4_reverse_nat"
   157  	cDefinesMap["LB4_SERVICES_MAP_V2"] = "cilium_lb4_services_v2"
   158  	cDefinesMap["LB4_RR_SEQ_MAP_V2"] = "cilium_lb4_rr_seq_v2"
   159  	cDefinesMap["LB4_BACKEND_MAP"] = "cilium_lb4_backends"
   160  	cDefinesMap["LB4_REVERSE_NAT_SK_MAP"] = "cilium_lb4_reverse_sk"
   161  
   162  	cDefinesMap["TRACE_PAYLOAD_LEN"] = fmt.Sprintf("%dULL", option.Config.TracePayloadlen)
   163  	cDefinesMap["MTU"] = fmt.Sprintf("%d", cfg.MtuConfig.GetDeviceMTU())
   164  
   165  	if option.Config.EnableIPv4 {
   166  		cDefinesMap["ENABLE_IPV4"] = "1"
   167  	}
   168  
   169  	if option.Config.EnableIPv6 {
   170  		cDefinesMap["ENABLE_IPV6"] = "1"
   171  	}
   172  
   173  	if option.Config.EnableIPSec {
   174  		cDefinesMap["ENABLE_IPSEC"] = "1"
   175  	}
   176  
   177  	if option.Config.InstallIptRules || iptables.KernelHasNetfilter() {
   178  		cDefinesMap["NO_REDIRECT"] = "1"
   179  	}
   180  
   181  	if option.Config.EncryptNode {
   182  		cDefinesMap["ENCRYPT_NODE"] = "1"
   183  	}
   184  
   185  	if option.Config.EnableHostReachableServices {
   186  		if option.Config.EnableHostServicesTCP {
   187  			cDefinesMap["ENABLE_HOST_SERVICES_TCP"] = "1"
   188  		}
   189  		if option.Config.EnableHostServicesUDP {
   190  			cDefinesMap["ENABLE_HOST_SERVICES_UDP"] = "1"
   191  		}
   192  		if option.Config.EnableHostServicesTCP && option.Config.EnableHostServicesUDP {
   193  			cDefinesMap["ENABLE_HOST_SERVICES_FULL"] = "1"
   194  		}
   195  	}
   196  
   197  	if option.Config.EncryptInterface != "" {
   198  		link, err := netlink.LinkByName(option.Config.EncryptInterface)
   199  		if err == nil {
   200  			cDefinesMap["ENCRYPT_IFACE"] = fmt.Sprintf("%d", link.Attrs().Index)
   201  
   202  			addr, err := netlink.AddrList(link, netlink.FAMILY_V4)
   203  			if err == nil {
   204  				a := byteorder.HostSliceToNetwork(addr[0].IPNet.IP, reflect.Uint32).(uint32)
   205  				cDefinesMap["IPV4_ENCRYPT_IFACE"] = fmt.Sprintf("%d", a)
   206  			}
   207  		}
   208  	}
   209  	if option.Config.IsPodSubnetsDefined() {
   210  		cDefinesMap["IP_POOLS"] = "1"
   211  	}
   212  	haveMasquerade := !option.Config.InstallIptRules && option.Config.Masquerade
   213  	if haveMasquerade || option.Config.EnableNodePort {
   214  		if option.Config.EnableIPv4 {
   215  			cDefinesMap["SNAT_MAPPING_IPV4"] = nat.MapNameSnat4Global
   216  			cDefinesMap["SNAT_MAPPING_IPV4_SIZE"] = fmt.Sprintf("%d", option.Config.NATMapEntriesGlobal)
   217  		}
   218  
   219  		if option.Config.EnableIPv6 {
   220  			cDefinesMap["SNAT_MAPPING_IPV6"] = nat.MapNameSnat6Global
   221  			cDefinesMap["SNAT_MAPPING_IPV6_SIZE"] = fmt.Sprintf("%d", option.Config.NATMapEntriesGlobal)
   222  		}
   223  	}
   224  	if haveMasquerade {
   225  		cDefinesMap["ENABLE_MASQUERADE"] = "1"
   226  		cDefinesMap["SNAT_MAPPING_MIN_PORT"] = fmt.Sprintf("%d", nat.MinPortSnatDefault)
   227  		cDefinesMap["SNAT_MAPPING_MAX_PORT"] = fmt.Sprintf("%d", nat.MaxPortSnatDefault)
   228  
   229  		// SNAT_DIRECTION is defined by init.sh
   230  		if option.Config.EnableIPv4 {
   231  			ipv4Addr := node.GetExternalIPv4()
   232  			cDefinesMap["SNAT_IPV4_EXTERNAL"] = fmt.Sprintf("%#x", byteorder.HostSliceToNetwork(ipv4Addr, reflect.Uint32).(uint32))
   233  		}
   234  
   235  		if option.Config.EnableIPv6 {
   236  			extraMacrosMap["SNAT_IPV6_EXTERNAL"] = hostIP.String()
   237  			fw.WriteString(defineIPv6("SNAT_IPV6_EXTERNAL", hostIP))
   238  		}
   239  	}
   240  
   241  	if (!option.Config.InstallIptRules && option.Config.Masquerade) || option.Config.EnableNodePort {
   242  		ctmap.WriteBPFMacros(fw, nil)
   243  	}
   244  
   245  	if option.Config.EnableNodePort {
   246  		cDefinesMap["ENABLE_NODEPORT"] = "1"
   247  		cDefinesMap["NODEPORT_PORT_MIN"] = fmt.Sprintf("%d", option.Config.NodePortMin)
   248  		cDefinesMap["NODEPORT_PORT_MAX"] = fmt.Sprintf("%d", option.Config.NodePortMax)
   249  		cDefinesMap["NODEPORT_PORT_MIN_NAT"] = fmt.Sprintf("%d", option.Config.NodePortMax+1)
   250  		cDefinesMap["NODEPORT_PORT_MAX_NAT"] = "65535"
   251  
   252  		if option.Config.EnableIPv4 {
   253  			cDefinesMap["NODEPORT_NEIGH4"] = "cilium_nodeport_neigh4"
   254  		}
   255  		if option.Config.EnableIPv6 {
   256  			cDefinesMap["NODEPORT_NEIGH6"] = "cilium_nodeport_neigh6"
   257  		}
   258  	}
   259  
   260  	// Since golang maps are unordered, we sort the keys in the map
   261  	// to get a consistent writtern format to the writer. This maintains
   262  	// the consistency when we try to calculate hash for a datapath after
   263  	// writing the config.
   264  	keys := []string{}
   265  	for key := range cDefinesMap {
   266  		keys = append(keys, key)
   267  	}
   268  	sort.Strings(keys)
   269  
   270  	for _, key := range keys {
   271  		fmt.Fprintf(fw, "#define %s %s\n", key, cDefinesMap[key])
   272  	}
   273  
   274  	// Populate cDefinesMap with extraMacrosMap to get all the configuration
   275  	// in the cDefinesMap itself.
   276  	for key, value := range extraMacrosMap {
   277  		cDefinesMap[key] = value
   278  	}
   279  
   280  	// Write the JSON encoded config as base64 encoded commented string to
   281  	// the header file.
   282  	jsonBytes, err := json.Marshal(cDefinesMap)
   283  	if err == nil {
   284  		// We don't care if some error occurs while marshaling the map.
   285  		// In such cases we skip embedding the base64 encoded JSON configuration
   286  		// to the writer.
   287  		encodedConfig := base64.StdEncoding.EncodeToString(jsonBytes)
   288  		fmt.Fprintf(fw, "\n// JSON_OUTPUT: %s\n", encodedConfig)
   289  	}
   290  
   291  	return fw.Flush()
   292  }
   293  
   294  func (l *linuxDatapath) writeNetdevConfig(w io.Writer, cfg datapath.DeviceConfiguration) {
   295  	fmt.Fprint(w, cfg.GetOptions().GetFmtList())
   296  	if option.Config.IsFlannelMasterDeviceSet() {
   297  		fmt.Fprint(w, "#define HOST_REDIRECT_TO_INGRESS 1\n")
   298  	}
   299  
   300  	// In case the Linux kernel doesn't support LPM map type, pass the set
   301  	// of prefix length for the datapath to lookup the map.
   302  	if ipcache.IPCache.MapType != bpf.BPF_MAP_TYPE_LPM_TRIE {
   303  		ipcachePrefixes6, ipcachePrefixes4 := cfg.GetCIDRPrefixLengths()
   304  
   305  		fmt.Fprint(w, "#define IPCACHE6_PREFIXES ")
   306  		for _, prefix := range ipcachePrefixes6 {
   307  			fmt.Fprintf(w, "%d,", prefix)
   308  		}
   309  		fmt.Fprint(w, "\n")
   310  		fmt.Fprint(w, "#define IPCACHE4_PREFIXES ")
   311  		for _, prefix := range ipcachePrefixes4 {
   312  			fmt.Fprintf(w, "%d,", prefix)
   313  		}
   314  		fmt.Fprint(w, "\n")
   315  	}
   316  }
   317  
   318  // WriteNetdevConfig writes the BPF configuration for the endpoint to a writer.
   319  func (l *linuxDatapath) WriteNetdevConfig(w io.Writer, cfg datapath.DeviceConfiguration) error {
   320  	fw := bufio.NewWriter(w)
   321  	l.writeNetdevConfig(fw, cfg)
   322  	return fw.Flush()
   323  }
   324  
   325  // writeStaticData writes the endpoint-specific static data defines to the
   326  // specified writer. This must be kept in sync with loader.ELFSubstitutions().
   327  func (l *linuxDatapath) writeStaticData(fw io.Writer, e datapath.EndpointConfiguration) {
   328  	fmt.Fprint(fw, defineIPv6("LXC_IP", e.IPv6Address()))
   329  	fmt.Fprint(fw, defineIPv4("LXC_IPV4", e.IPv4Address()))
   330  
   331  	fmt.Fprint(fw, defineMAC("NODE_MAC", e.GetNodeMAC()))
   332  	fmt.Fprint(fw, defineUint32("LXC_ID", uint32(e.GetID())))
   333  
   334  	secID := e.GetIdentityLocked().Uint32()
   335  	fmt.Fprintf(fw, defineUint32("SECLABEL", secID))
   336  	fmt.Fprintf(fw, defineUint32("SECLABEL_NB", byteorder.HostToNetwork(secID).(uint32)))
   337  
   338  	epID := uint16(e.GetID())
   339  	fmt.Fprintf(fw, "#define POLICY_MAP %s\n", bpf.LocalMapName(policymap.MapName, epID))
   340  	fmt.Fprintf(fw, "#define CALLS_MAP %s\n", bpf.LocalMapName("cilium_calls_", epID))
   341  	fmt.Fprintf(fw, "#define CONFIG_MAP %s\n", bpf.LocalMapName(bpfconfig.MapNamePrefix, epID))
   342  }
   343  
   344  // WriteEndpointConfig writes the BPF configuration for the endpoint to a writer.
   345  func (l *linuxDatapath) WriteEndpointConfig(w io.Writer, e datapath.EndpointConfiguration) error {
   346  	fw := bufio.NewWriter(w)
   347  
   348  	writeIncludes(w)
   349  	l.writeStaticData(fw, e)
   350  
   351  	return l.writeTemplateConfig(fw, e)
   352  }
   353  
   354  func (l *linuxDatapath) writeTemplateConfig(fw *bufio.Writer, e datapath.EndpointConfiguration) error {
   355  	if e.RequireEgressProg() {
   356  		fmt.Fprintf(fw, "#define USE_BPF_PROG_FOR_INGRESS_POLICY 1\n")
   357  	}
   358  
   359  	if option.Config.ForceLocalPolicyEvalAtSource {
   360  		fmt.Fprintf(fw, "#define FORCE_LOCAL_POLICY_EVAL_AT_SOURCE 1\n")
   361  	}
   362  
   363  	if e.RequireRouting() {
   364  		fmt.Fprintf(fw, "#define ENABLE_ROUTING 1\n")
   365  	}
   366  
   367  	if !e.HasIpvlanDataPath() {
   368  		if e.RequireARPPassthrough() {
   369  			fmt.Fprint(fw, "#define ENABLE_ARP_PASSTHROUGH 1\n")
   370  		} else {
   371  			fmt.Fprint(fw, "#define ENABLE_ARP_RESPONDER 1\n")
   372  		}
   373  
   374  		fmt.Fprint(fw, "#define ENABLE_HOST_REDIRECT 1\n")
   375  		if option.Config.IsFlannelMasterDeviceSet() {
   376  			fmt.Fprint(fw, "#define HOST_REDIRECT_TO_INGRESS 1\n")
   377  		}
   378  	}
   379  
   380  	if e.ConntrackLocalLocked() {
   381  		ctmap.WriteBPFMacros(fw, e)
   382  	} else {
   383  		ctmap.WriteBPFMacros(fw, nil)
   384  	}
   385  
   386  	// Always enable L4 and L3 load balancer for now
   387  	fmt.Fprint(fw, "#define LB_L3 1\n")
   388  	fmt.Fprint(fw, "#define LB_L4 1\n")
   389  
   390  	// Local delivery metrics should always be set for endpoint programs.
   391  	fmt.Fprint(fw, "#define LOCAL_DELIVERY_METRICS 1\n")
   392  
   393  	l.writeNetdevConfig(fw, e)
   394  
   395  	return fw.Flush()
   396  }
   397  
   398  // WriteTemplateConfig writes the BPF configuration for the template to a writer.
   399  func (l *linuxDatapath) WriteTemplateConfig(w io.Writer, e datapath.EndpointConfiguration) error {
   400  	fw := bufio.NewWriter(w)
   401  	return l.writeTemplateConfig(fw, e)
   402  }