Skip to content

Commit

Permalink
Merge pull request #1100 from caseydavenport/ipv6-optional
Browse files Browse the repository at this point in the history
Add manual override for IPv6 support
  • Loading branch information
fasaxc committed Sep 16, 2016
2 parents a0894a1 + 1e0b696 commit 2753d68
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 12 deletions.
20 changes: 17 additions & 3 deletions calico/felix/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,21 @@ def __init__(self, config_path):
"default value opens the ssh port, 22.",
[22], value_is_int_list=True)
self.add_parameter("FailsafeOutboundHostPorts",
"Comma-separated list of numeric TCP ports to allow"
"traffic to from all host endpoints. Useful to "
"avoid accidentally cutting off, for example, "
"Comma-separated list of numeric TCP ports to "
"allow traffic to from all host endpoints. Useful "
"to avoid accidentally cutting off, for example, "
"access to etcd. The default value allows "
"connectivity to etcd's default ports "
"2379,2380,4001 and 7001.",
[2379,2380,4001,7001], value_is_int_list=True)
self.add_parameter("Ipv6Support",
"Whether IPv6 support is enabled. If 'true', Felix "
"will program ip6tables rules and any IPv6 routes; "
"if 'false', Felix will not provide any IPv6 "
"function. If set to 'auto', Felix will attempt "
"to detect whether the system supports IPv6 and "
"use it if it does.",
"auto")

# The following setting determines which flavour of Iptables Generator
# plugin is loaded. Note: this plugin support is currently highly
Expand Down Expand Up @@ -438,6 +446,7 @@ def _finish_update(self, final=False):
self.IGNORE_LOOSE_RPF = self.parameters["IgnoreLooseRPF"].value
self.CLUSTER_GUID = self.parameters["ClusterGUID"].value
self.USAGE_REPORT = self.parameters["UsageReportingEnabled"].value
self.IPV6_SUPPORT = self.parameters["Ipv6Support"].value.lower()

self._validate_cfg(final=final)

Expand Down Expand Up @@ -773,6 +782,11 @@ def _validate_cfg(self, final=True):
raise ConfigException("Out-of-range port %s" % p,
self.parameters[name])

if self.IPV6_SUPPORT not in ("true", "false", "auto"):
log.warning("Unrecognized value for Ipv6Support (%s), "
"defaulting to 'auto'", self.IPV6_SUPPORT)
self.IPV6_SUPPORT = "auto"

if not final:
# Do not check that unset parameters are defaulted; we have more
# config to read.
Expand Down
11 changes: 10 additions & 1 deletion calico/felix/felix.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,16 @@ def _main_greenlet(config):
v4_fip_manager,
]

v6_enabled, ipv6_reason = futils.ipv6_supported()
# Determine if ipv6 is enabled using the config option.
if config.IPV6_SUPPORT == "true":
v6_enabled = True
ipv6_reason = None
elif config.IPV6_SUPPORT == "auto":
v6_enabled, ipv6_reason = futils.detect_ipv6_supported()
else:
v6_enabled = False
ipv6_reason = "Ipv6Support is 'false'"

if v6_enabled:
v6_raw_updater = IptablesUpdater("raw", ip_version=6, config=config)
v6_filter_updater = IptablesUpdater("filter", ip_version=6,
Expand Down
9 changes: 8 additions & 1 deletion calico/felix/futils.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ def find_set_bits(mask):
mask = next_mask


def ipv6_supported():
def detect_ipv6_supported():
"""Checks whether we can support IPv6 on this host.
:returns tuple[bool,str]: supported, reason for lack of support or None.
Expand All @@ -541,6 +541,13 @@ def ipv6_supported():
return False, ("ip6tables not installed; Calico IPv6 support requires "
"Linux kernel v3.3 or above and ip6tables v1.4.14 or "
"above.")

# Check for the existence of the IPv6 NAT table.
try:
check_call(["ip6tables-save", "--table", "nat"])
except FailedSystemCall:
return False, "Failed to load IPv6 NAT table"

try:
# Use -C, which checks for a particular rule. We don't expect the rule
# to exist but iptables will give us a distinctive error if the
Expand Down
6 changes: 6 additions & 0 deletions calico/felix/test/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def test_default_config(self):
self.assertEqual(config.REPORTING_TTL_SECS, 90)
self.assertEqual(config.IPTABLES_MARK_MASK, 0xff000000)
self.assertEqual(config.IPTABLES_MARK_ACCEPT, "0x1000000")
self.assertEqual(config.IPV6_SUPPORT, "auto")

def test_bad_plugin_name(self):
env_dict = {"FELIX_IPTABLESGENERATORPLUGIN": "unknown"}
Expand All @@ -105,6 +106,11 @@ def test_bad_plugin_name(self):
'"calico.felix.iptables_generator".'):
config = load_config("felix_default.cfg", env_dict=env_dict)

def test_bad_ipv6_support_value(self):
env_dict = {"FELIX_IPV6SUPPORT": "badvalue"}
config = load_config("felix_default.cfg", env_dict=env_dict)
self.assertEqual(config.IPV6_SUPPORT, "auto")

def test_invalid_port(self):
data = { "felix_invalid_port.cfg": "Invalid port in field",
"felix_invalid_addr.cfg": "Invalid or unresolvable",
Expand Down
21 changes: 14 additions & 7 deletions calico/felix/test/test_futils.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def assert_safe_truncate(self, s, length, expected):
self.assertEqual(result, expected,
"Expected %r to be truncated as %r but got %r" %
(s, expected, result))

def test_longest_prefix(self):
self.assertEqual(futils.find_longest_prefix([]), None)
self.assertEqual(futils.find_longest_prefix(["a"]), "a")
Expand All @@ -152,18 +152,18 @@ def test_longest_prefix(self):
@mock.patch("os.path.exists", autospec=True)
@mock.patch("calico.felix.futils.check_call", autospec=True)
@mock.patch("calico.felix.futils.Popen", autospec=True)
def test_ipv6_supported(self, m_popen, m_check_call, m_exists):
def test_detect_ipv6_supported(self, m_popen, m_check_call, m_exists):
m_popen.return_value.communicate.return_value = "", ""
m_exists.return_value = True
self.assertEqual(futils.ipv6_supported(), (True, None))
self.assertEqual(futils.detect_ipv6_supported(), (True, None))

@mock.patch("os.path.exists", autospec=True)
@mock.patch("calico.felix.futils.check_call", autospec=True)
@mock.patch("calico.felix.futils.Popen", autospec=True)
def test_ipv6_compiled_out(self, m_popen, m_check_call, m_exists):
m_popen.return_value.communicate.return_value = "", ""
m_exists.return_value = False
self.assertEqual(futils.ipv6_supported(), (False, mock.ANY))
self.assertEqual(futils.detect_ipv6_supported(), (False, mock.ANY))

@mock.patch("os.path.exists", autospec=True)
@mock.patch("calico.felix.futils.check_call", autospec=True)
Expand All @@ -172,7 +172,7 @@ def test_ipv6_missing_ip6tables(self, m_popen, m_check_call, m_exists):
m_popen.return_value.communicate.return_value = "", ""
m_exists.return_value = True
m_check_call.side_effect = futils.FailedSystemCall()
self.assertEqual(futils.ipv6_supported(), (False, mock.ANY))
self.assertEqual(futils.detect_ipv6_supported(), (False, mock.ANY))

@mock.patch("os.path.exists", autospec=True)
@mock.patch("calico.felix.futils.check_call", autospec=True)
Expand All @@ -184,15 +184,22 @@ def test_ipv6_missing_rpfilter(self, m_popen, m_check_call, m_exists):
"ip6tables vA.B.C: Couldn't load match `rpfilter':No such file or "
"directory"
)
self.assertEqual(futils.ipv6_supported(), (False, mock.ANY))
self.assertEqual(futils.detect_ipv6_supported(), (False, mock.ANY))

@mock.patch("os.path.exists", autospec=True)
@mock.patch("calico.felix.futils.check_call", autospec=True)
@mock.patch("calico.felix.futils.Popen", autospec=True)
def test_ipv6_missing_rpfilter_error(self, m_popen, m_check_call, m_exists):
m_exists.return_value = True
m_popen.side_effect = OSError()
self.assertEqual(futils.ipv6_supported(), (False, mock.ANY))
self.assertEqual(futils.detect_ipv6_supported(), (False, mock.ANY))

@mock.patch("os.path.exists", autospec=True)
@mock.patch("calico.felix.futils.check_call", autospec=True)
def test_ipv6_missing_nat_table(self, m_check_call, m_exists):
m_exists.return_value = True
m_check_call.side_effect = iter([None, futils.FailedSystemCall()])
self.assertEqual(futils.detect_ipv6_supported(), (False, mock.ANY))

@mock.patch("calico.felix.futils.urllib3.disable_warnings", autospec=True)
@mock.patch("calico.felix.futils.urllib3.util.retry.Retry", autospec=True)
Expand Down
5 changes: 5 additions & 0 deletions docs/source/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ The full list of parameters which can be set is as follows.
| | | cutting off a host with incorrect configuration. The default value opens etcd's standard |
| | | ports to ensure that Felix does not get cut off from etcd. |
+----------------------------------+---------------------------------------+-------------------------------------------------------------------------------------------+
| Ipv6Support | auto | Whether IPv6 support is enabled. If 'true', Felix will program ip6tables rules |
| | | and any IPv6 routes; if 'false', Felix will not provide any IPv6 function. If set |
| | | to 'auto', Felix will attempt to detect whether the system supports |
| | | IPv6 and use it if it does. |
+----------------------------------+---------------------------------------+-------------------------------------------------------------------------------------------+


Environment variables
Expand Down

0 comments on commit 2753d68

Please sign in to comment.