patching service unicast messaging for loopback interface

IPv6 does not support multicast messaging on the loopback interface.
Therefore to support patch agent to controller messaging when the
management network is associated with the loopback interface it
must operate using unicast messaging.  This is sufficient since it
does not need to communicate with multiple process instances when
operating in this local mode.

Change-Id: I95efd06be9717a76ccbd543c1fd16408fd89dbeb
Closes-Bug: #1830779
Signed-off-by: Matt Peters <matt.peters@windriver.com>
This commit is contained in:
Matt Peters 2019-05-30 14:45:26 -04:00
parent 4b1e9e453f
commit 319252db74
5 changed files with 65 additions and 31 deletions

View File

@ -64,18 +64,19 @@ class PatchService:
self.sock_out.bind((mgmt_ip, 0)) self.sock_out.bind((mgmt_ip, 0))
self.sock_in.bind(('', self.port)) self.sock_in.bind(('', self.port))
# These options are for outgoing multicast messages if self.mcast_addr:
self.sock_out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, interface_addr) # These options are for outgoing multicast messages
self.sock_out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1) self.sock_out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, interface_addr)
# Since only the controllers are sending to this address, self.sock_out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
# we want the loopback so the local agent can receive it # Since only the controllers are sending to this address,
self.sock_out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1) # we want the loopback so the local agent can receive it
self.sock_out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
# Register the multicast group # Register the multicast group
group = socket.inet_pton(socket.AF_INET, self.mcast_addr) group = socket.inet_pton(socket.AF_INET, self.mcast_addr)
mreq = struct.pack('=4s4s', group, interface_addr) mreq = struct.pack('=4s4s', group, interface_addr)
self.sock_in.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) self.sock_in.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
return self.sock_in return self.sock_in
@ -106,18 +107,19 @@ class PatchService:
self.sock_out.bind((mgmt_ip, 0)) self.sock_out.bind((mgmt_ip, 0))
self.sock_in.bind(('', self.port)) self.sock_in.bind(('', self.port))
# These options are for outgoing multicast messages if self.mcast_addr:
mgmt_ifindex = utils.if_nametoindex(cfg.get_mgmt_iface()) # These options are for outgoing multicast messages
self.sock_out.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, mgmt_ifindex) mgmt_ifindex = utils.if_nametoindex(cfg.get_mgmt_iface())
self.sock_out.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 1) self.sock_out.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, mgmt_ifindex)
# Since only the controllers are sending to this address, self.sock_out.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 1)
# we want the loopback so the local agent can receive it # Since only the controllers are sending to this address,
self.sock_out.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, 1) # we want the loopback so the local agent can receive it
self.sock_out.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, 1)
# Register the multicast group # Register the multicast group
if_index_packed = struct.pack('I', mgmt_ifindex) if_index_packed = struct.pack('I', mgmt_ifindex)
group = socket.inet_pton(socket.AF_INET6, self.mcast_addr) + if_index_packed group = socket.inet_pton(socket.AF_INET6, self.mcast_addr) + if_index_packed
self.sock_in.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group) self.sock_in.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group)
return self.sock_in return self.sock_in
@ -145,6 +147,10 @@ class PatchService:
return None return None
def audit_socket(self): def audit_socket(self):
if not self.mcast_addr:
# Multicast address not configured, therefore nothing to do
return
# Ensure multicast address is still allocated # Ensure multicast address is still allocated
cmd = "ip maddr show %s | awk 'BEGIN {ORS=\"\"}; {if ($2 == \"%s\") print $2}'" % \ cmd = "ip maddr show %s | awk 'BEGIN {ORS=\"\"}; {if ($2 == \"%s\") print $2}'" % \
(cfg.get_mgmt_iface(), self.mcast_addr) (cfg.get_mgmt_iface(), self.mcast_addr)

View File

@ -114,7 +114,6 @@ def get_mgmt_iface():
try: try:
value = str(config.get('platform_conf', 'management_interface')) value = str(config.get('platform_conf', 'management_interface'))
global nodetype
mgmt_if = value mgmt_if = value
platform_conf_mtime = os.stat(tsc.PLATFORM_CONF_FILE).st_mtime platform_conf_mtime = os.stat(tsc.PLATFORM_CONF_FILE).st_mtime

View File

@ -43,3 +43,5 @@ CLI_OPT_RECURSIVE = '--recursive'
CLI_OPT_RELEASE = '--release' CLI_OPT_RELEASE = '--release'
ENABLE_DEV_CERTIFICATE_PATCH_IDENTIFIER = 'ENABLE_DEV_CERTIFICATE' ENABLE_DEV_CERTIFICATE_PATCH_IDENTIFIER = 'ENABLE_DEV_CERTIFICATE'
LOOPBACK_INTERFACE_NAME = "lo"

View File

@ -172,9 +172,10 @@ class PatchMessageHelloAgentAck(messages.PatchMessage):
LOG.error("Should not get here") LOG.error("Should not get here")
def send(self, sock): def send(self, sock):
global pa
self.encode() self.encode()
message = json.dumps(self.message) message = json.dumps(self.message)
sock.sendto(message, (cfg.controller_mcast_group, cfg.controller_port)) sock.sendto(message, (pa.controller_address, cfg.controller_port))
class PatchMessageQueryDetailed(messages.PatchMessage): class PatchMessageQueryDetailed(messages.PatchMessage):
@ -292,6 +293,7 @@ class PatchAgent(PatchService):
PatchService.__init__(self) PatchService.__init__(self)
self.sock_out = None self.sock_out = None
self.sock_in = None self.sock_in = None
self.controller_address = None
self.listener = None self.listener = None
self.changes = False self.changes = False
self.installed = {} self.installed = {}
@ -323,8 +325,15 @@ class PatchAgent(PatchService):
if self.port != cfg.agent_port: if self.port != cfg.agent_port:
self.port = cfg.agent_port self.port = cfg.agent_port
if self.mcast_addr != cfg.agent_mcast_group: # Loopback interface does not support multicast messaging, therefore
# revert to using unicast messaging when configured against the
# loopback device
if cfg.get_mgmt_iface() == constants.LOOPBACK_INTERFACE_NAME:
self.mcast_addr = None
self.controller_address = cfg.get_mgmt_ip()
else:
self.mcast_addr = cfg.agent_mcast_group self.mcast_addr = cfg.agent_mcast_group
self.controller_address = cfg.controller_mcast_group
def setup_tcp_socket(self): def setup_tcp_socket(self):
address_family = utils.get_management_family() address_family = utils.get_management_family()

View File

@ -230,9 +230,10 @@ class PatchMessageHello(messages.PatchMessage):
resp.send(sock) resp.send(sock)
def send(self, sock): def send(self, sock):
global pc
self.encode() self.encode()
message = json.dumps(self.message) message = json.dumps(self.message)
sock.sendto(message, (cfg.controller_mcast_group, cfg.controller_port)) sock.sendto(message, (pc.controller_address, cfg.controller_port))
class PatchMessageHelloAck(messages.PatchMessage): class PatchMessageHelloAck(messages.PatchMessage):
@ -254,9 +255,10 @@ class PatchMessageHelloAck(messages.PatchMessage):
pc.controller_neighbours_lock.release() pc.controller_neighbours_lock.release()
def send(self, sock): def send(self, sock):
global pc
self.encode() self.encode()
message = json.dumps(self.message) message = json.dumps(self.message)
sock.sendto(message, (cfg.controller_mcast_group, cfg.controller_port)) sock.sendto(message, (pc.controller_address, cfg.controller_port))
class PatchMessageSyncReq(messages.PatchMessage): class PatchMessageSyncReq(messages.PatchMessage):
@ -283,10 +285,11 @@ class PatchMessageSyncReq(messages.PatchMessage):
resp.send(sock) resp.send(sock)
def send(self, sock): def send(self, sock):
global pc
LOG.info("sending sync req") LOG.info("sending sync req")
self.encode() self.encode()
message = json.dumps(self.message) message = json.dumps(self.message)
sock.sendto(message, (cfg.controller_mcast_group, cfg.controller_port)) sock.sendto(message, (pc.controller_address, cfg.controller_port))
class PatchMessageSyncComplete(messages.PatchMessage): class PatchMessageSyncComplete(messages.PatchMessage):
@ -309,10 +312,11 @@ class PatchMessageSyncComplete(messages.PatchMessage):
pc.controller_neighbours_lock.release() pc.controller_neighbours_lock.release()
def send(self, sock): def send(self, sock):
global pc
LOG.info("sending sync complete") LOG.info("sending sync complete")
self.encode() self.encode()
message = json.dumps(self.message) message = json.dumps(self.message)
sock.sendto(message, (cfg.controller_mcast_group, cfg.controller_port)) sock.sendto(message, (pc.controller_address, cfg.controller_port))
class PatchMessageHelloAgent(messages.PatchMessage): class PatchMessageHelloAgent(messages.PatchMessage):
@ -328,10 +332,11 @@ class PatchMessageHelloAgent(messages.PatchMessage):
LOG.error("Should not get here") LOG.error("Should not get here")
def send(self, sock): def send(self, sock):
global pc
self.encode() self.encode()
message = json.dumps(self.message) message = json.dumps(self.message)
local_hostname = utils.ip_to_versioned_localhost(cfg.agent_mcast_group) local_hostname = utils.ip_to_versioned_localhost(cfg.agent_mcast_group)
sock.sendto(message, (cfg.agent_mcast_group, cfg.agent_port)) sock.sendto(message, (pc.agent_address, cfg.agent_port))
sock.sendto(message, (local_hostname, cfg.agent_port)) sock.sendto(message, (local_hostname, cfg.agent_port))
@ -549,9 +554,10 @@ class PatchMessageDropHostReq(messages.PatchMessage):
return return
def send(self, sock): def send(self, sock):
global pc
self.encode() self.encode()
message = json.dumps(self.message) message = json.dumps(self.message)
sock.sendto(message, (cfg.controller_mcast_group, cfg.controller_port)) sock.sendto(message, (pc.controller_address, cfg.controller_port))
class PatchController(PatchService): class PatchController(PatchService):
@ -577,6 +583,8 @@ class PatchController(PatchService):
self.sock_out = None self.sock_out = None
self.sock_in = None self.sock_in = None
self.controller_address = None
self.agent_address = None
self.patch_op_counter = 1 self.patch_op_counter = 1
self.patch_data = PatchData() self.patch_data = PatchData()
self.patch_data.load_all() self.patch_data.load_all()
@ -605,8 +613,18 @@ class PatchController(PatchService):
if self.port != cfg.controller_port: if self.port != cfg.controller_port:
self.port = cfg.controller_port self.port = cfg.controller_port
if self.mcast_addr != cfg.controller_mcast_group: # Loopback interface does not support multicast messaging, therefore
# revert to using unicast messaging when configured against the
# loopback device
if cfg.get_mgmt_iface() == constants.LOOPBACK_INTERFACE_NAME:
mgmt_ip = cfg.get_mgmt_ip()
self.mcast_addr = None
self.controller_address = mgmt_ip
self.agent_address = mgmt_ip
else:
self.mcast_addr = cfg.controller_mcast_group self.mcast_addr = cfg.controller_mcast_group
self.controller_address = cfg.controller_mcast_group
self.agent_address = cfg.agent_mcast_group
def socket_lock_acquire(self): def socket_lock_acquire(self):
self.socket_lock.acquire() self.socket_lock.acquire()