diff -crB --new-file cilium-1.6.6-backup/daemon/daemon_main.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/daemon/daemon_main.go
*** cilium-1.6.6-backup/daemon/daemon_main.go	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/daemon/daemon_main.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 54,59 ****
--- 54,62 ----
  	"github.com/cilium/cilium/pkg/loadinfo"
  	"github.com/cilium/cilium/pkg/logging"
  	"github.com/cilium/cilium/pkg/logging/logfields"
+ 	"github.com/cilium/cilium/pkg/maps/ctmap"
+ 	"github.com/cilium/cilium/pkg/maps/nat"
+ 	"github.com/cilium/cilium/pkg/maps/policymap"
  	ipcachemap "github.com/cilium/cilium/pkg/maps/ipcache"
  	"github.com/cilium/cilium/pkg/metrics"
  	monitorAPI "github.com/cilium/cilium/pkg/monitor/api"
***************
*** 807,812 ****
--- 810,818 ----
  	flags.Int(option.PolicyMapEntriesName, defaults.PolicyMapEntries, "Maximum number of entries in endpoint policy map (per endpoint)")
  	option.BindEnv(option.PolicyMapEntriesName)
  
+ 	flags.Float64(option.MapEntriesGlobalDynamicSizeRatioName, 0.0, "Ratio (0.0-1.0) of total system memory to use for dynamic sizing of CT, NAT and policy BPF maps. Set to 0.0 to disable dynamic BPF map sizing (default: 0.0)")
+ 	option.BindEnv(option.MapEntriesGlobalDynamicSizeRatioName)
+ 
  	flags.String(option.CMDRef, "", "Path to cmdref output directory")
  	flags.MarkHidden(option.CMDRef)
  	option.BindEnv(option.CMDRef)
***************
*** 937,942 ****
--- 943,955 ----
  }
  
  func initEnv(cmd *cobra.Command) {
+ 	option.Config.SetMapElementSizes(
+ 		// for the conntrack and NAT element size we assume the largest possible
+ 		// key size, i.e. IPv6 keys
+ 		ctmap.SizeofCtKey6Global+ctmap.SizeofCtEntry,
+ 		nat.SizeofNatKey6+nat.SizeofNatEntry6,
+ 		policymap.SizeofPolicyKey+policymap.SizeofPolicyEntry)
+ 
  	// Prepopulate option.Config with options from CLI.
  	option.Config.Populate()
  
diff -crB --new-file cilium-1.6.6-backup/Documentation/architecture.rst cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/Documentation/architecture.rst
*** cilium-1.6.6-backup/Documentation/architecture.rst	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/Documentation/architecture.rst	2020-05-03 22:28:03.235402599 -0700
***************
*** 45,51 ****
    tc ingress hook can be coupled with above XDP hook. When this is done it
    is reasonable to assume that the majority of the traffic at this
    point is legitimate and destined for the host.
!   
    Containers typically use a virtual device called a veth pair which acts
    as a virtual wire connecting the container to the host. By attaching to
    the TC ingress hook of the host side of this veth pair Cilium can monitor
--- 45,51 ----
    tc ingress hook can be coupled with above XDP hook. When this is done it
    is reasonable to assume that the majority of the traffic at this
    point is legitimate and destined for the host.
! 
    Containers typically use a virtual device called a veth pair which acts
    as a virtual wire connecting the container to the host. By attaching to
    the TC ingress hook of the host side of this veth pair Cilium can monitor
***************
*** 54,60 ****
    network traffic to the host side virtual devices with another BPF program
    attached to the tc ingress hook as well Cilium can monitor and enforce
    policy on all traffic entering or exiting the node.
!   
    Depending on the use case, containers may also be connected through ipvlan
    devices instead of a veth pair. In this mode, the physical device in the
    host is the ipvlan master where virtual ipvlan devices in slave mode are
--- 54,60 ----
    network traffic to the host side virtual devices with another BPF program
    attached to the tc ingress hook as well Cilium can monitor and enforce
    policy on all traffic entering or exiting the node.
! 
    Depending on the use case, containers may also be connected through ipvlan
    devices instead of a veth pair. In this mode, the physical device in the
    host is the ipvlan master where virtual ipvlan devices in slave mode are
***************
*** 257,262 ****
--- 257,273 ----
  Tunnel                   node             64k             Max 32k nodes (IPv4+IPv6) or 64k nodes (IPv4 or IPv6) across all clusters
  ======================== ================ =============== =====================================================
  
+ For some BPF maps, the upper capacity limit can be overridden using command
+ line options for ``cilium-agent``. A given capacity can be set using
+ ``--bpf-ct-global-tcp-max``, ``--bpf-ct-global-any-max``,
+ ``--bpf-nat-global-max``, ``--bpf-policy-map-max``, and
+ ``--bpf-fragments-map-max``.
+ 
+ Using ``--bpf-map-dynamic-size-ratio`` the upper capacity limits of the
+ connection tracking, NAT, and policy maps are determined at agent startup based
+ on the given ratio of the total system memory. For example a given ratio of 0.03
+ leads to 3% of the total system memory to be used for these maps.
+ 
  Kubernetes Integration
  ======================
  
diff -crB --new-file cilium-1.6.6-backup/Documentation/cmdref/cilium-agent.md cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/Documentation/cmdref/cilium-agent.md
*** cilium-1.6.6-backup/Documentation/cmdref/cilium-agent.md	2020-05-03 21:42:31.178633913 -0700
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/Documentation/cmdref/cilium-agent.md	2020-05-03 22:28:03.235402599 -0700
***************
*** 31,36 ****
--- 31,37 ----
        --bpf-ct-timeout-regular-tcp-syn duration               Establishment timeout for entries in TCP CT table (default 1m0s)
        --bpf-ct-timeout-service-any duration                   Timeout for service entries in non-TCP CT table (default 1m0s)
        --bpf-ct-timeout-service-tcp duration                   Timeout for established service entries in TCP CT table (default 6h0m0s)
+       --bpf-map-dynamic-size-ratio float                      Ratio (0.0-1.0) of total system memory to use for dynamic sizing of CT, NAT and policy BPF maps. Set to 0.0 to disable dynamic BPF map sizing (default: 0.0)
        --bpf-nat-global-max int                                Maximum number of entries for the global BPF NAT table (default 524288)
        --bpf-policy-map-max int                                Maximum number of entries in endpoint policy map (per endpoint) (default 16384)
        --bpf-root string                                       Path to BPF filesystem
diff -crB --new-file cilium-1.6.6-backup/Documentation/install/upgrade.rst cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/Documentation/install/upgrade.rst
*** cilium-1.6.6-backup/Documentation/install/upgrade.rst	2020-05-03 21:42:31.178633913 -0700
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/Documentation/install/upgrade.rst	2020-05-03 22:28:03.235402599 -0700
***************
*** 409,414 ****
--- 409,429 ----
      default ConfigMap. Existing deployments will continue to use ``kvstore``
      unless opted into new behavior via the ConfigMap.
  
+   * ``bpf-map-dynamic-size-ratio`` has been added to allow sizing of the TCP CT,
+     non-TCP CT, NAT and policy BPF maps based on the total system memory. This
+     option allows to specify a ratio (0.0-1.0) of total system memory to use for
+     these maps. On new installations, this ratio is set to 0.03 by default,
+     leading to 3% of the total system memory to be allocated for these maps. On
+     a node with 4 GiB of total system memory this ratio corresponds
+     approximately to the default BPF map sizes. A value of 0.0 will disable
+     sizing of the BPF maps based on system memory. Any BPF map sizes configured
+     manually using the ``ctTcpMax``, ``ctAnyMax``, ``natMax`` options will take
+     precedence over the dynamically determined value.
+ 
+     On upgrades of existing installations, this option is disable by default,
+     i.e. it is set to 0.0. Users wanting to use this feature need to enable it
+     explicitly in their `ConfigMap`, see section :ref:`upgrade_configmap`.
+ 
  Deprecated options
  ~~~~~~~~~~~~~~~~~~
  
diff -crB --new-file cilium-1.6.6-backup/Documentation/spelling_wordlist.txt cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/Documentation/spelling_wordlist.txt
*** cilium-1.6.6-backup/Documentation/spelling_wordlist.txt	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/Documentation/spelling_wordlist.txt	2020-05-03 22:28:03.235402599 -0700
***************
*** 203,208 ****
--- 203,209 ----
  gettingstarted
  getpeername
  github
+ GiB
  Github
  githubusercontent
  glibc
diff -crB --new-file cilium-1.6.6-backup/install/kubernetes/cilium/charts/config/templates/configmap.yaml cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/install/kubernetes/cilium/charts/config/templates/configmap.yaml
*** cilium-1.6.6-backup/install/kubernetes/cilium/charts/config/templates/configmap.yaml	2020-05-03 21:42:31.178633913 -0700
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/install/kubernetes/cilium/charts/config/templates/configmap.yaml	2020-05-03 22:28:03.235402599 -0700
***************
*** 120,125 ****
--- 120,129 ----
    # table.
    bpf-nat-global-max: "{{ .Values.global.bpf.natMax }}"
  
+   # Specifies the ratio (0.0-1.0) of total system memory to use for dynamic
+   # sizing of the TCP CT, non-TCP CT, NAT and policy BPF maps.
+   bpf-map-dynamic-size-ratio: "{{ .Values.global.bpf.mapDynamicSizeRatio }}"
+ 
    # Pre-allocation of map entries allows per-packet latency to be reduced, at
    # the expense of up-front memory allocation for the entries in the maps. The
    # default value below will minimize memory usage in the default installation;
diff -crB --new-file cilium-1.6.6-backup/install/kubernetes/cilium/values.yaml cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/install/kubernetes/cilium/values.yaml
*** cilium-1.6.6-backup/install/kubernetes/cilium/values.yaml	2020-05-03 21:42:31.178633913 -0700
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/install/kubernetes/cilium/values.yaml	2020-05-03 22:28:03.235402599 -0700
***************
*** 186,191 ****
--- 186,198 ----
      # natMax is the maximum number of entries for the NAT table
      natMax: 524288 
  
+     # mapDynamicSizeRatio is the ratio (0.0-1.0) of total system memory to use
+     # for dynamic sizing of CT, NAT and policy BPF maps. If set to 0.0, dynamic
+     # sizing of BPF maps is disabled. The default value of 0.03 (3%) leads to
+     # approximately the default BPF map sizes on a node with 4 GiB of total
+     # system memory.
+     mapDynamicSizeRatio: 0.03
+ 
      # montiorAggregation is the level of aggregation for datapath trace events
      monitorAggregation: medium
  
diff -crB --new-file cilium-1.6.6-backup/install/kubernetes/quick-install.yaml cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/install/kubernetes/quick-install.yaml
*** cilium-1.6.6-backup/install/kubernetes/quick-install.yaml	2020-05-03 21:42:31.178633913 -0700
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/install/kubernetes/quick-install.yaml	2020-05-03 22:28:03.235402599 -0700
***************
*** 53,58 ****
--- 53,62 ----
    # table.
    bpf-nat-global-max: "524288"
  
+   # Specifies the ratio (0.0-1.0) of total system memory to use for dynamic
+   # sizing of the TCP CT, non-TCP CT, NAT and policy BPF maps.
+   bpf-map-dynamic-size-ratio: "0.03"
+ 
    # Pre-allocation of map entries allows per-packet latency to be reduced, at
    # the expense of up-front memory allocation for the entries in the maps. The
    # default value below will minimize memory usage in the default installation;
diff -crB --new-file cilium-1.6.6-backup/pkg/maps/ctmap/ctmap.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/ctmap/ctmap.go
*** cilium-1.6.6-backup/pkg/maps/ctmap/ctmap.go	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/ctmap/ctmap.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 116,122 ****
  		keySize:   keySize,
  		// the value type is CtEntry for all CT maps
  		mapValue:   &CtEntry{},
! 		valueSize:  int(unsafe.Sizeof(CtEntry{})),
  		maxEntries: maxEntries,
  		parser:     bpf.ConvertKeyValue,
  		natMap:     nat,
--- 116,122 ----
  		keySize:   keySize,
  		// the value type is CtEntry for all CT maps
  		mapValue:   &CtEntry{},
! 		valueSize:  SizeofCtEntry,
  		maxEntries: maxEntries,
  		parser:     bpf.ConvertKeyValue,
  		natMap:     nat,
diff -crB --new-file cilium-1.6.6-backup/pkg/maps/ctmap/types.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/ctmap/types.go
*** cilium-1.6.6-backup/pkg/maps/ctmap/types.go	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/ctmap/types.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 381,386 ****
--- 381,388 ----
  	tuple.TupleKey6Global
  }
  
+ const SizeofCtKey6Global = int(unsafe.Sizeof(CtKey6Global{}))
+ 
  // NewValue creates a new bpf.MapValue.
  func (k *CtKey6Global) NewValue() bpf.MapValue { return &CtEntry{} }
  
***************
*** 483,488 ****
--- 485,492 ----
  	LastRxReport     uint32 `align:"last_rx_report"`
  }
  
+ const SizeofCtEntry = int(unsafe.Sizeof(CtEntry{}))
+ 
  // GetValuePtr returns the unsafe.Pointer for s.
  func (c *CtEntry) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(c) }
  
diff -crB --new-file cilium-1.6.6-backup/pkg/maps/nat/ipv4.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/nat/ipv4.go
*** cilium-1.6.6-backup/pkg/maps/nat/ipv4.go	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/nat/ipv4.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 35,40 ****
--- 35,43 ----
  	Port      uint16     `align:"to_sport"`
  }
  
+ // SizeofNatEntry4 is the size of the NatEntry4 type in bytes.
+ const SizeofNatEntry4 = int(unsafe.Sizeof(NatEntry4{}))
+ 
  // GetValuePtr returns the unsafe.Pointer for n.
  func (n *NatEntry4) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(n) }
  
diff -crB --new-file cilium-1.6.6-backup/pkg/maps/nat/ipv6.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/nat/ipv6.go
*** cilium-1.6.6-backup/pkg/maps/nat/ipv6.go	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/nat/ipv6.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 35,40 ****
--- 35,43 ----
  	Port      uint16     `align:"to_sport"`
  }
  
+ // SizeofNatEntry6 is the size of the NatEntry6 type in bytes.
+ const SizeofNatEntry6 = int(unsafe.Sizeof(NatEntry6{}))
+ 
  // GetValuePtr returns the unsafe.Pointer for n.
  func (n *NatEntry6) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(n) }
  
diff -crB --new-file cilium-1.6.6-backup/pkg/maps/nat/nat.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/nat/nat.go
*** cilium-1.6.6-backup/pkg/maps/nat/nat.go	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/nat/nat.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 77,90 ****
  
  	if v4 {
  		mapKey = &NatKey4{}
! 		sizeKey = int(unsafe.Sizeof(NatKey4{}))
  		mapValue = &NatEntry4{}
! 		sizeVal = int(unsafe.Sizeof(NatEntry4{}))
  	} else {
  		mapKey = &NatKey6{}
! 		sizeKey = int(unsafe.Sizeof(NatKey6{}))
  		mapValue = &NatEntry6{}
! 		sizeVal = int(unsafe.Sizeof(NatEntry6{}))
  	}
  	return &Map{
  		Map: *bpf.NewMap(
--- 77,90 ----
  
  	if v4 {
  		mapKey = &NatKey4{}
! 		sizeKey = SizeofNatKey4
  		mapValue = &NatEntry4{}
! 		sizeVal = SizeofNatEntry4
  	} else {
  		mapKey = &NatKey6{}
! 		sizeKey = SizeofNatKey6
  		mapValue = &NatEntry6{}
! 		sizeVal = SizeofNatEntry6
  	}
  	return &Map{
  		Map: *bpf.NewMap(
diff -crB --new-file cilium-1.6.6-backup/pkg/maps/nat/types.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/nat/types.go
*** cilium-1.6.6-backup/pkg/maps/nat/types.go	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/nat/types.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 45,50 ****
--- 45,53 ----
  	tuple.TupleKey4Global
  }
  
+ // SizeofNatKey4 is the size of the NatKey4 type in bytes.
+ const SizeofNatKey4 = int(unsafe.Sizeof(NatKey4{}))
+ 
  // NewValue creates a new bpf.MapValue.
  func (k *NatKey4) NewValue() bpf.MapValue { return &NatEntry4{} }
  
***************
*** 80,85 ****
--- 83,91 ----
  	tuple.TupleKey6Global
  }
  
+ // SizeofNatKey6 is the size of the NatKey6 type in bytes.
+ const SizeofNatKey6 = int(unsafe.Sizeof(NatKey6{}))
+ 
  // NewValue creates a new bpf.MapValue.
  func (k *NatKey6) NewValue() bpf.MapValue { return &NatEntry4{} }
  
diff -crB --new-file cilium-1.6.6-backup/pkg/maps/policymap/policymap.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/policymap/policymap.go
*** cilium-1.6.6-backup/pkg/maps/policymap/policymap.go	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/maps/policymap/policymap.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 76,81 ****
--- 76,84 ----
  	TrafficDirection uint8
  }
  
+ // SizeofPolicyKey is the size of type PolicyKey.
+ const SizeofPolicyKey = int(unsafe.Sizeof(PolicyKey{}))
+ 
  // PolicyEntry represents an entry in the BPF policy map for an endpoint. It must
  // match the layout of policy_entry in bpf/lib/common.h.
  // +k8s:deepcopy-gen=true
***************
*** 89,94 ****
--- 92,100 ----
  	Bytes     uint64
  }
  
+ // SizeofPolicyEntry is the size of type PolicyEntry.
+ const SizeofPolicyEntry = int(unsafe.Sizeof(PolicyEntry{}))
+ 
  func (pe *PolicyEntry) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(pe) }
  func (pe *PolicyEntry) NewValue() bpf.MapValue      { return &PolicyEntry{} }
  
***************
*** 264,272 ****
  			path,
  			mapType,
  			&PolicyKey{},
! 			int(unsafe.Sizeof(PolicyKey{})),
  			&PolicyEntry{},
! 			int(unsafe.Sizeof(PolicyEntry{})),
  			MaxEntries,
  			flags, 0,
  			bpf.ConvertKeyValue,
--- 270,278 ----
  			path,
  			mapType,
  			&PolicyKey{},
! 			SizeofPolicyKey,
  			&PolicyEntry{},
! 			SizeofPolicyEntry,
  			MaxEntries,
  			flags, 0,
  			bpf.ConvertKeyValue,
diff -crB --new-file cilium-1.6.6-backup/pkg/option/config.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/option/config.go
*** cilium-1.6.6-backup/pkg/option/config.go	2020-05-03 21:42:31.178633913 -0700
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/option/config.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 38,43 ****
--- 38,44 ----
  	"github.com/cilium/cilium/pkg/metrics"
  
  	"github.com/prometheus/client_golang/prometheus"
+ 	"github.com/shirou/gopsutil/mem"
  	"github.com/sirupsen/logrus"
  	"github.com/spf13/viper"
  )
***************
*** 407,412 ****
--- 408,418 ----
  	// and is 2/3 of the full CT size as a heuristic
  	NATMapEntriesGlobalDefault = int((CTMapEntriesGlobalTCPDefault + CTMapEntriesGlobalAnyDefault) * 2 / 3)
  
+ 	// MapEntriesGlobalDynamicSizeRatioName is the name of the option to
+ 	// set the ratio of total system memory to use for dynamic sizing of the
+ 	// CT, NAT and policy BPF maps.
+ 	MapEntriesGlobalDynamicSizeRatioName = "bpf-map-dynamic-size-ratio"
+ 
  	// LimitTableMin defines the minimum CT or NAT table limit
  	LimitTableMin = 1 << 10 // 1Ki entries
  
***************
*** 1239,1244 ****
--- 1245,1260 ----
  	// Enabling this option reduces waste of IP addresses but may increase
  	// the number of API calls to AWS EC2 service.
  	AwsReleaseExcessIps bool
+ 
+ 	// sizeofCTElement is the size of an element (key + value) in the CT map.
+ 	sizeofCTElement int
+ 
+ 	// sizeofNATElement is the size of an element (key + value) in the NAT map.
+ 	sizeofNATElement int
+ 
+ 	// sizeofPolicyElement is the size of an element (key + value) in the
+ 	// policy map.
+ 	sizeofPolicyElement int
  }
  
  var (
***************
*** 1562,1570 ****
  	c.AnnotateK8sNode = viper.GetBool(AnnotateK8sNode)
  	c.AutoCreateCiliumNodeResource = viper.GetBool(AutoCreateCiliumNodeResource)
  	c.BPFCompilationDebug = viper.GetBool(BPFCompileDebugName)
- 	c.CTMapEntriesGlobalTCP = viper.GetInt(CTMapEntriesGlobalTCPName)
- 	c.CTMapEntriesGlobalAny = viper.GetInt(CTMapEntriesGlobalAnyName)
- 	c.NATMapEntriesGlobal = viper.GetInt(NATMapEntriesGlobalName)
  	c.BPFRoot = viper.GetString(BPFRoot)
  	c.CGroupRoot = viper.GetString(CGroupRoot)
  	c.ClusterID = viper.GetInt(ClusterIDName)
--- 1578,1583 ----
***************
*** 1649,1655 ****
  	c.FlannelMasterDevice = viper.GetString(FlannelMasterDevice)
  	c.FlannelUninstallOnExit = viper.GetBool(FlannelUninstallOnExit)
  	c.FlannelManageExistingContainers = viper.GetBool(FlannelManageExistingContainers)
- 	c.PolicyMapEntries = viper.GetInt(PolicyMapEntriesName)
  	c.PProf = viper.GetBool(PProf)
  	c.PreAllocateMaps = viper.GetBool(PreAllocateMapsName)
  	c.PrependIptablesChains = viper.GetBool(PrependIptablesChainsName)
--- 1662,1667 ----
***************
*** 1680,1685 ****
--- 1692,1701 ----
  		c.ipv4NativeRoutingCIDR = cidr.MustParseCIDR(nativeCIDR)
  	}
  
+ 	if err := c.calculateBPFMapSizes(); err != nil {
+ 		log.Fatal(err)
+ 	}
+ 
  	// toFQDNs options
  	// When the poller is enabled, the default MinTTL is lowered. This is to
  	// avoid caching large sets of identities generated by a poller (it runs
***************
*** 1841,1846 ****
--- 1857,1974 ----
  	c.AwsReleaseExcessIps = viper.GetBool(AwsReleaseExcessIps)
  }
  
+ func (c *DaemonConfig) calculateBPFMapSizes() error {
+ 	// BPF map size options
+ 	// Any map size explicitly set via option will override the dynamic
+ 	// sizing.
+ 	c.CTMapEntriesGlobalTCP = viper.GetInt(CTMapEntriesGlobalTCPName)
+ 	c.CTMapEntriesGlobalAny = viper.GetInt(CTMapEntriesGlobalAnyName)
+ 	c.NATMapEntriesGlobal = viper.GetInt(NATMapEntriesGlobalName)
+ 	c.PolicyMapEntries = viper.GetInt(PolicyMapEntriesName)
+ 
+ 	// Don't attempt dynamic sizing if any of the sizeof members was not
+ 	// populated by the daemon (or any other caller).
+ 	if c.sizeofCTElement == 0 || c.sizeofNATElement == 0 || c.sizeofPolicyElement == 0 {
+ 		return nil
+ 	}
+ 
+ 	// Allow the range (0.0, 1.0] because the dynamic size will anyway be
+ 	// clamped to the table limits. Thus, a ratio of e.g. 0.98 will not lead
+ 	// to 98% of the total memory being allocated for BPF maps.
+ 	dynamicSizeRatio := viper.GetFloat64(MapEntriesGlobalDynamicSizeRatioName)
+ 	if 0.0 < dynamicSizeRatio && dynamicSizeRatio <= 1.0 {
+ 		vms, err := mem.VirtualMemory()
+ 		if err != nil || vms == nil {
+ 			log.WithError(err).Fatal("Failed to get system memory")
+ 		}
+ 		c.calculateDynamicBPFMapSizes(vms.Total, dynamicSizeRatio)
+ 	} else if dynamicSizeRatio < 0.0 {
+ 		return fmt.Errorf("specified dynamic map size ratio %f must be ≥ 0.0", dynamicSizeRatio)
+ 	} else if dynamicSizeRatio > 1.0 {
+ 		return fmt.Errorf("specified dynamic map size ratio %f must be ≤ 1.0", dynamicSizeRatio)
+ 	}
+ 	return nil
+ }
+ 
+ // SetMapElementSizes sets the BPF map element sizes (key + value) used for
+ // dynamic BPF map size calculations in calculateDynamicBPFMapSizes.
+ func (c *DaemonConfig) SetMapElementSizes(sizeofCTElement, sizeofNATElement, sizeofPolicyElement int) {
+ 	c.sizeofCTElement = sizeofCTElement
+ 	c.sizeofNATElement = sizeofNATElement
+ 	c.sizeofPolicyElement = sizeofPolicyElement
+ }
+ 
+ func (c *DaemonConfig) calculateDynamicBPFMapSizes(totalMemory uint64, dynamicSizeRatio float64) {
+ 	// Heuristic:
+ 	// Distribute relative to map default entries among the different maps.
+ 	// Cap each map size by the maximum. Map size provided by the user will
+ 	// override the calculated value and also the max. There will be a check
+ 	// for maximum size later on in DaemonConfig.Validate()
+ 	//
+ 	// Calculation examples:
+ 	//
+ 	// Memory   CT TCP  CT Any      NAT  Policy
+ 	//
+ 	//  512MB    33140   16570    33140    1035
+ 	//    1GB    66280   33140    66280    2071
+ 	//    4GB   265121  132560   265121    8285
+ 	//   16GB  1060485  530242  1060485   33140
+ 	memoryAvailableForMaps := int(float64(totalMemory) * dynamicSizeRatio)
+ 	log.Debugf("Memory available for map entries (%.3f of %d): %d", dynamicSizeRatio, totalMemory, memoryAvailableForMaps)
+ 	totalMapMemoryDefault := CTMapEntriesGlobalTCPDefault*c.sizeofCTElement +
+ 		CTMapEntriesGlobalAnyDefault*c.sizeofCTElement +
+ 		NATMapEntriesGlobalDefault*c.sizeofNATElement +
+ 		defaults.PolicyMapEntries*c.sizeofPolicyElement
+ 	log.Debugf("Total memory for default map entries: %d", totalMapMemoryDefault)
+ 
+ 	getEntries := func(entriesDefault, min, max int) int {
+ 		entries := (entriesDefault * memoryAvailableForMaps) / totalMapMemoryDefault
+ 		if entries < min {
+ 			entries = min
+ 		} else if entries > max {
+ 			log.Debugf("clamped from %d to %d", entries, max)
+ 			entries = max
+ 		}
+ 		return entries
+ 	}
+ 
+ 	// If value for a particular map was explicitly set by an
+ 	// option, disable dynamic sizing for this map and use the
+ 	// provided size.
+ 	if !viper.IsSet(CTMapEntriesGlobalTCPName) {
+ 		c.CTMapEntriesGlobalTCP =
+ 			getEntries(CTMapEntriesGlobalTCPDefault, LimitTableMin, LimitTableMax)
+ 		log.Debugf("option %s set by dynamic sizing to %v (default %v)",
+ 			CTMapEntriesGlobalTCPName, c.CTMapEntriesGlobalTCP, CTMapEntriesGlobalTCPDefault)
+ 	} else {
+ 		log.Debugf("option %s set by user to %v", CTMapEntriesGlobalTCPName, c.CTMapEntriesGlobalTCP)
+ 	}
+ 	if !viper.IsSet(CTMapEntriesGlobalAnyName) {
+ 		c.CTMapEntriesGlobalAny =
+ 			getEntries(CTMapEntriesGlobalAnyDefault, LimitTableMin, LimitTableMax)
+ 		log.Debugf("option %s set by dynamic sizing to %v (default %v)",
+ 			CTMapEntriesGlobalAnyName, c.CTMapEntriesGlobalAny, CTMapEntriesGlobalAnyDefault)
+ 	} else {
+ 		log.Debugf("option %s set by user to %v", CTMapEntriesGlobalAnyName, c.CTMapEntriesGlobalAny)
+ 	}
+ 	if !viper.IsSet(NATMapEntriesGlobalName) {
+ 		c.NATMapEntriesGlobal =
+ 			getEntries(NATMapEntriesGlobalDefault, LimitTableMin, LimitTableMax)
+ 		log.Debugf("option %s set by dynamic sizing to %v (default %v)",
+ 			NATMapEntriesGlobalName, c.NATMapEntriesGlobal, NATMapEntriesGlobalDefault)
+ 	} else {
+ 		log.Debugf("option %s set by user to %v", NATMapEntriesGlobalName, c.NATMapEntriesGlobal)
+ 	}
+ 	if !viper.IsSet(PolicyMapEntriesName) {
+ 		c.PolicyMapEntries =
+ 			getEntries(defaults.PolicyMapEntries, PolicyMapMin, PolicyMapMax)
+ 		log.Debugf("option %s set by dynamic sizing to %v (default %v)",
+ 			PolicyMapEntriesName, c.PolicyMapEntries, defaults.PolicyMapEntries)
+ 	} else {
+ 		log.Debugf("option %s set by user to %v", PolicyMapEntriesName, c.PolicyMapEntries)
+ 	}
+ }
+ 
  func sanitizeIntParam(paramName string, paramDefault int) int {
  	intParam := viper.GetInt(paramName)
  	if intParam <= 0 {
diff -crB --new-file cilium-1.6.6-backup/pkg/option/config_test.go cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/option/config_test.go
*** cilium-1.6.6-backup/pkg/option/config_test.go	2020-02-03 14:04:55.000000000 -0800
--- cilium-1.6.6-add-option-to-dynamically-size-bpf-maps/pkg/option/config_test.go	2020-05-03 22:28:03.235402599 -0700
***************
*** 24,29 ****
--- 24,31 ----
  	"path/filepath"
  	"testing"
  
+ 	"github.com/cilium/cilium/pkg/defaults"
+ 	"github.com/google/go-cmp/cmp"
  	flag "github.com/spf13/pflag"
  	"github.com/spf13/viper"
  	. "gopkg.in/check.v1"
***************
*** 250,252 ****
--- 252,429 ----
  	c.Assert(d.IsExcludedLocalAddress(net.ParseIP("f00d::1")), Equals, true)
  	c.Assert(d.IsExcludedLocalAddress(net.ParseIP("f00d::2")), Equals, false)
  }
+ 
+ const (
+ 	_   = iota
+ 	KiB = 1 << (10 * iota)
+ 	MiB
+ 	GiB
+ )
+ 
+ func TestBPFMapSizeCalculation(t *testing.T) {
+ 	type sizes struct {
+ 		CTMapSizeTCP  int
+ 		CTMapSizeAny  int
+ 		NATMapSize    int
+ 		PolicyMapSize int
+ 	}
+ 	tests := []struct {
+ 		name        string
+ 		totalMemory uint64
+ 		ratio       float64
+ 		want        sizes
+ 		preTestRun  func()
+ 	}{
+ 		{
+ 			name: "static default sizes",
+ 			// zero memory and ratio: skip calculateDynamicBPFMapSizes
+ 			want: sizes{
+ 				CTMapSizeTCP:  CTMapEntriesGlobalTCPDefault,
+ 				CTMapSizeAny:  CTMapEntriesGlobalAnyDefault,
+ 				NATMapSize:    NATMapEntriesGlobalDefault,
+ 				PolicyMapSize: defaults.PolicyMapEntries,
+ 			},
+ 			preTestRun: func() {
+ 				viper.Set(CTMapEntriesGlobalTCPName, CTMapEntriesGlobalTCPDefault)
+ 				viper.Set(CTMapEntriesGlobalAnyName, CTMapEntriesGlobalAnyDefault)
+ 				viper.Set(NATMapEntriesGlobalName, NATMapEntriesGlobalDefault)
+ 				viper.Set(PolicyMapEntriesName, defaults.PolicyMapEntries)
+ 			},
+ 		},
+ 		{
+ 			name: "static, non-default sizes inside range",
+ 			// zero memory and ratio: skip calculateDynamicBPFMapSizes
+ 			want: sizes{
+ 				CTMapSizeTCP:  CTMapEntriesGlobalTCPDefault + 128,
+ 				CTMapSizeAny:  CTMapEntriesGlobalAnyDefault - 64,
+ 				NATMapSize:    NATMapEntriesGlobalDefault + 256,
+ 				PolicyMapSize: defaults.PolicyMapEntries - 32,
+ 			},
+ 			preTestRun: func() {
+ 				viper.Set(CTMapEntriesGlobalTCPName, CTMapEntriesGlobalTCPDefault+128)
+ 				viper.Set(CTMapEntriesGlobalAnyName, CTMapEntriesGlobalAnyDefault-64)
+ 				viper.Set(NATMapEntriesGlobalName, NATMapEntriesGlobalDefault+256)
+ 				viper.Set(PolicyMapEntriesName, defaults.PolicyMapEntries-32)
+ 			},
+ 		},
+ 		{
+ 			name:        "dynamic size without any static sizes (512MB, 3%)",
+ 			totalMemory: 512 * MiB,
+ 			ratio:       0.03,
+ 			want: sizes{
+ 				CTMapSizeTCP:  68246,
+ 				CTMapSizeAny:  34123,
+ 				NATMapSize:    68246,
+ 				PolicyMapSize: 2132,
+ 			},
+ 		},
+ 		{
+ 			name:        "dynamic size without any static sizes (1GiB, 3%)",
+ 			totalMemory: 1 * GiB,
+ 			ratio:       0.03,
+ 			want: sizes{
+ 				CTMapSizeTCP:  136492,
+ 				CTMapSizeAny:  68246,
+ 				NATMapSize:    136492,
+ 				PolicyMapSize: 4265,
+ 			},
+ 		},
+ 		{
+ 			name:        "dynamic size without any static sizes (2GiB, 3%)",
+ 			totalMemory: 2 * GiB,
+ 			ratio:       0.03,
+ 			want: sizes{
+ 				CTMapSizeTCP:  272985,
+ 				CTMapSizeAny:  136492,
+ 				NATMapSize:    272985,
+ 				PolicyMapSize: 8530,
+ 			},
+ 		},
+ 		{
+ 			name:        "dynamic size without any static sizes (4GiB, 3%)",
+ 			totalMemory: 4 * GiB,
+ 			ratio:       0.03,
+ 			want: sizes{
+ 				CTMapSizeTCP:  545970,
+ 				CTMapSizeAny:  272985,
+ 				NATMapSize:    545970,
+ 				PolicyMapSize: 17061,
+ 			},
+ 		},
+ 		{
+ 			name:        "dynamic size without any static sizes (16GiB, 3%)",
+ 			totalMemory: 16 * GiB,
+ 			ratio:       0.03,
+ 			want: sizes{
+ 				CTMapSizeTCP:  2183881,
+ 				CTMapSizeAny:  1091940,
+ 				NATMapSize:    2183881,
+ 				PolicyMapSize: PolicyMapMax,
+ 			},
+ 		},
+ 		{
+ 			name:        "dynamic size with static CT TCP size (4GiB, 2.5%)",
+ 			totalMemory: 4 * GiB,
+ 			ratio:       0.025,
+ 			want: sizes{
+ 				CTMapSizeTCP:  CTMapEntriesGlobalTCPDefault + 1024,
+ 				CTMapSizeAny:  227487,
+ 				NATMapSize:    454975,
+ 				PolicyMapSize: 14217,
+ 			},
+ 			preTestRun: func() {
+ 				viper.Set(CTMapEntriesGlobalTCPName, CTMapEntriesGlobalTCPDefault+1024)
+ 			},
+ 		},
+ 		{
+ 			name:        "huge dynamic size ratio gets clamped (8GiB, 98%)",
+ 			totalMemory: 16 * GiB,
+ 			ratio:       0.98,
+ 			want: sizes{
+ 				CTMapSizeTCP:  LimitTableMax,
+ 				CTMapSizeAny:  LimitTableMax,
+ 				NATMapSize:    LimitTableMax,
+ 				PolicyMapSize: PolicyMapMax,
+ 			},
+ 		},
+ 	}
+ 
+ 	for _, tt := range tests {
+ 		t.Run(tt.name, func(t *testing.T) {
+ 			viper.Reset()
+ 
+ 			if tt.preTestRun != nil {
+ 				tt.preTestRun()
+ 			}
+ 
+ 			d := &DaemonConfig{
+ 				CTMapEntriesGlobalTCP: viper.GetInt(CTMapEntriesGlobalTCPName),
+ 				CTMapEntriesGlobalAny: viper.GetInt(CTMapEntriesGlobalAnyName),
+ 				NATMapEntriesGlobal:   viper.GetInt(NATMapEntriesGlobalName),
+ 				PolicyMapEntries:      viper.GetInt(PolicyMapEntriesName),
+ 			}
+ 			// cannot set these from the Sizeof* consts from
+ 			// pkg/maps/* due to circular dependencies.
+ 			d.SetMapElementSizes(
+ 				94, // ctmap.SizeofCTKey + policymap.SizeofCTEntry
+ 				94, // nat.SizeofNATKey + nat.SizeofNATEntry
+ 				32, // policymap.SizeofPolicyKey + policymap.SizeofPolicyEntry
+ 			)
+ 
+ 			if tt.totalMemory > 0 && tt.ratio > 0.0 {
+ 				d.calculateDynamicBPFMapSizes(tt.totalMemory, tt.ratio)
+ 			}
+ 
+ 			got := sizes{
+ 				d.CTMapEntriesGlobalTCP,
+ 				d.CTMapEntriesGlobalAny,
+ 				d.NATMapEntriesGlobal,
+ 				d.PolicyMapEntries,
+ 			}
+ 
+ 			if diff := cmp.Diff(tt.want, got); diff != "" {
+ 				t.Errorf("DaemonConfig.calculateDynamicBPFMapSize (-want +got):\n%s", diff)
+ 			}
+ 		})
+ 	}
+ }
