[swss]: Reduce tunnel_packet_handler memory usage (#9762)

* Configure scapy to not store sniffed packets

Signed-off-by: Lawrence Lee <lawlee@microsoft.com>
This commit is contained in:
Lawrence Lee 2022-02-07 11:55:48 -08:00 committed by Qi Luo
parent 62ad4bf3bb
commit 59a7dc9f1e

View File

@ -9,15 +9,17 @@ destination IP to trigger the process of obtaining neighbor information
""" """
import subprocess import subprocess
import time import time
from datetime import datetime from datetime import datetime
from ipaddress import ip_interface from ipaddress import ip_interface
from swsssdk import ConfigDBConnector, SonicV2Connector
from sonic_py_common import logger as log
from pyroute2 import IPRoute from pyroute2 import IPRoute
from scapy.layers.inet import IP from scapy.layers.inet import IP
from scapy.layers.inet6 import IPv6 from scapy.layers.inet6 import IPv6
from scapy.sendrecv import AsyncSniffer from scapy.sendrecv import AsyncSniffer
from swsssdk import ConfigDBConnector, SonicV2Connector
from sonic_py_common import logger as log
logger = log.Logger() logger = log.Logger()
@ -36,6 +38,10 @@ RTM_NEWLINK = 'RTM_NEWLINK'
class TunnelPacketHandler(object): class TunnelPacketHandler(object):
"""
This class handles unroutable tunnel packets that are trapped
to the CPU from the ASIC.
"""
def __init__(self): def __init__(self):
self.config_db = ConfigDBConnector() self.config_db = ConfigDBConnector()
@ -68,19 +74,32 @@ class TunnelPacketHandler(object):
return self._portchannel_intfs return self._portchannel_intfs
def get_portchannel_index_mapping(self): def get_intf_name(self, msg):
""" """
Gets a mapping of interface kernel indices to portchannel interfaces Gets the interface name for a netlink msg
Returns:
(str) The interface name, or the empty string if no interface
name was found
"""
attr_list = msg.get('attrs', list())
for attribute, val in attr_list:
if attribute == 'IFLA_IFNAME':
return val
return ''
def netlink_msg_is_for_portchannel(self, msg):
"""
Determines if a netlink message is about a PortChannel interface
Returns: Returns:
(list) integers representing kernel indices (list) integers representing kernel indices
""" """
index_map = {} ifname = self.get_intf_name(msg)
for portchannel in self.portchannel_intfs:
index = self.netlink_api.link_lookup(ifname=portchannel[0])[0]
index_map[index] = portchannel
return index_map return ifname in [name for name, _ in self.portchannel_intfs]
def get_up_portchannels(self): def get_up_portchannels(self):
""" """
@ -89,15 +108,16 @@ class TunnelPacketHandler(object):
Returns: Returns:
(list) of interface names which are up, as strings (list) of interface names which are up, as strings
""" """
pc_index_map = self.get_portchannel_index_mapping() portchannel_intf_names = [name for name, _ in self.portchannel_intfs]
pc_indices = list(pc_index_map.keys()) link_statuses = []
link_statuses = self.netlink_api.get_links(*pc_indices) for intf in portchannel_intf_names:
status = self.netlink_api.link("get", ifname=intf)
link_statuses.append(status[0])
up_portchannels = [] up_portchannels = []
for status in link_statuses: for status in link_statuses:
if status['state'] == 'up': if status['state'] == 'up':
port_index = status['index'] up_portchannels.append(self.get_intf_name(status))
up_portchannels.append(pc_index_map[port_index][0])
return up_portchannels return up_portchannels
@ -117,7 +137,7 @@ class TunnelPacketHandler(object):
STATE_DB, STATE_DB,
intf_table_name, intf_table_name,
STATE_KEY STATE_KEY
) )
if intf_state and intf_state.lower() != 'ok': if intf_state and intf_state.lower() != 'ok':
return False return False
@ -177,13 +197,13 @@ class TunnelPacketHandler(object):
tunnel_type = tunnel_table[TUNNEL_TYPE_KEY].lower() tunnel_type = tunnel_table[TUNNEL_TYPE_KEY].lower()
self_loopback_ip = tunnel_table[DST_IP_KEY] self_loopback_ip = tunnel_table[DST_IP_KEY]
peer_loopback_ip = self.config_db.get_entry( peer_loopback_ip = self.config_db.get_entry(
PEER_SWITCH_TABLE, peer_switch PEER_SWITCH_TABLE, peer_switch
)[ADDRESS_IPV4_KEY] )[ADDRESS_IPV4_KEY]
except KeyError as e: except KeyError as error:
logger.log_warning( logger.log_warning(
'PEER_SWITCH or TUNNEL table missing data, ' 'PEER_SWITCH or TUNNEL table missing data, '
'could not find key {}' 'could not find key {}'
.format(e) .format(error)
) )
return None, None return None, None
@ -242,12 +262,11 @@ class TunnelPacketHandler(object):
come back up, we need to restart the sniffer to be able come back up, we need to restart the sniffer to be able
to sniff traffic on the interface that has come back up. to sniff traffic on the interface that has come back up.
""" """
pc_index_map = self.get_portchannel_index_mapping()
for msg in messages: for msg in messages:
if msg['index'] in pc_index_map: if self.netlink_msg_is_for_portchannel(msg):
if msg['state'] == 'up': if msg['state'] == 'up':
logger.log_info('{} came back up, sniffer restart required' logger.log_info('{} came back up, sniffer restart required'
.format(pc_index_map[msg['index']])) .format(self.get_intf_name(msg)))
return True return True
return False return False
@ -293,8 +312,8 @@ class TunnelPacketHandler(object):
sniffer = AsyncSniffer( sniffer = AsyncSniffer(
iface=sniff_intfs, iface=sniff_intfs,
filter=packet_filter, filter=packet_filter,
prn=_ping_inner_dst prn=_ping_inner_dst,
store=0
) )
sniffer.start() sniffer.start()
while True: while True:
@ -307,11 +326,15 @@ class TunnelPacketHandler(object):
sniffer = AsyncSniffer( sniffer = AsyncSniffer(
iface=sniff_intfs, iface=sniff_intfs,
filter=packet_filter, filter=packet_filter,
prn=_ping_inner_dst prn=_ping_inner_dst,
store=0
) )
sniffer.start() sniffer.start()
def run(self): def run(self):
"""
Entry point for the TunnelPacketHandler class
"""
self.wait_for_portchannels() self.wait_for_portchannels()
self.listen_for_tunnel_pkts() self.listen_for_tunnel_pkts()