Send both gratuitous ARP REQUESTs and REPLYs

Due to a linux kernel bug [1], network peers risk missing a gratuitous
arp update. This can be safely worked around by enabling arp_accept for
network interfaces that may receive those ARP packets [2]. Sadly, due to
another kernel bug [3], gratuitous ARP packets that we send are not
honoured by arp_accept. If we switch from REPLY to REQUEST packets, then
all recent kernels should enforce ARP table entry override on gARP
received.

REPLYs are left for backwards compatibility in case some network peers
honor REPLYs and not REQUESTs.

[1] https://patchwork.ozlabs.org/patch/760372/
[2] https://bugs.launchpad.net/fuel/+bug/1456272
[3] https://patchwork.ozlabs.org/patch/760373/

Related-Bug: #1690165
Change-Id: Iacd01875bf1299ff68fb5d1009d72088270320bb
(cherry picked from commit 82831b9d8d)
This commit is contained in:
Ihar Hrachyshka 2017-05-10 09:05:53 -07:00
parent d8c495cfc6
commit 5f3552e4d1
2 changed files with 35 additions and 27 deletions

View File

@ -1051,26 +1051,33 @@ def _arping(ns_name, iface_name, address, count, log_exception):
time.sleep(2)
first = False
# Pass -w to set timeout to ensure exit if interface removed while
# running
arping_cmd = ['arping', '-A', '-I', iface_name, '-c', 1,
'-w', 1.5, address]
try:
ip_wrapper = IPWrapper(namespace=ns_name)
# Since arping is used to send gratuitous ARP, a response is not
# expected. In some cases (no response) and with some platforms
# (>=Ubuntu 14.04), arping exit code can be 1.
ip_wrapper.netns.execute(arping_cmd, extra_ok_codes=[1])
except Exception as exc:
msg = _("Failed sending gratuitous ARP "
"to %(addr)s on %(iface)s in namespace %(ns)s: %(err)s")
logger_method = LOG.exception
if not log_exception:
logger_method = LOG.warning
logger_method(msg, {'addr': address,
'iface': iface_name,
'ns': ns_name,
'err': exc})
# some Linux kernels* don't honour REPLYs. Send both gratuitous REQUEST
# and REPLY packets (REQUESTs are left for backwards compatibility for
# in case if some network peers, vice versa, honor REPLYs and not
# REQUESTs)
#
# * https://patchwork.ozlabs.org/patch/763016/
for arg in ('-U', '-A'):
arping_cmd = ['arping', arg, '-I', iface_name, '-c', 1,
# Pass -w to set timeout to ensure exit if interface
# removed while running
'-w', 1.5, address]
try:
ip_wrapper = IPWrapper(namespace=ns_name)
# Since arping is used to send gratuitous ARP, a response is
# not expected. In some cases (no response) and with some
# platforms (>=Ubuntu 14.04), arping exit code can be 1.
ip_wrapper.netns.execute(arping_cmd, extra_ok_codes=[1])
except Exception as exc:
msg = _("Failed sending gratuitous ARP to %(addr)s on "
"%(iface)s in namespace %(ns)s: %(err)s")
logger_method = LOG.exception
if not log_exception:
logger_method = LOG.warning
logger_method(msg, {'addr': address,
'iface': iface_name,
'ns': ns_name,
'err': exc})
def send_ip_addr_adv_notif(

View File

@ -1407,13 +1407,14 @@ class TestArpPing(TestIPCmdBase):
ip_wrapper = mIPWrapper(namespace=mock.sentinel.ns_name)
# Just test that arping is called with the right arguments
arping_cmd = ['arping', '-A',
'-I', mock.sentinel.iface_name,
'-c', 1,
'-w', mock.ANY,
address]
ip_wrapper.netns.execute.assert_any_call(arping_cmd,
extra_ok_codes=[1])
for arg in ('-A', '-U'):
arping_cmd = ['arping', arg,
'-I', mock.sentinel.iface_name,
'-c', 1,
'-w', mock.ANY,
address]
ip_wrapper.netns.execute.assert_any_call(arping_cmd,
extra_ok_codes=[1])
@mock.patch('eventlet.spawn_n')
def test_no_ipv6_addr_notif(self, spawn_n):