def __send_sync_pkt(self, addr, pkt_args, sock=None, vteps=None): """ Generates and sends a VXFLD sync pkt. to peer SNDs. :param addr: tuple composed of the recipients' IP addr and port :param pkt_args: maps inner packet attributes to their values :param sock: socket object :param vteps: maps VNIs to tuples composed of a VTEP's IP address, holdtime and node identifier :type vteps: dict[int, (str, int, int)] :returns: a socket object :raises: socket.error """ response_type = pkt_args.get('response_type', VXFLD.ResponseType.NONE) vxfld_pkt = VXFLD.Packet(type=VXFLD.MsgType.SYNC, inner={'response_type': response_type}) if vteps is not None: vxfld_pkt.data.vni_vteps = vteps try: if sock is None: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(addr) sock.sendall(str(vxfld_pkt)) sock.shutdown(socket.SHUT_WR) return sock except socket.error as ex: # Socket not ready, buffer overflow etc. if sock is not None: sock.close() self._logger.error('Failed to send sync pkt: %s', ex) raise
def __handle_vxfld_msg(self, pkt, addr): """ HER: Handles a VXFLD message. :param pkt: socket buffer :param addr: tuple composed of the sender's address and port """ # pylint: disable=no-member srcip, _ = addr self.__last_response = int(time.time()) try: vxfld_pkt = VXFLD.Packet(pkt) except Exception as ex: # pylint: disable=broad-except self._logger.error('Unknown VXFLD packet received from %s: %s', srcip, ex.message) return refresh_pkt = vxfld_pkt.data if vxfld_pkt.type != VXFLD.MsgType.REFRESH: self._logger.warning('Unexpected vxfld pkt of type %d', vxfld_pkt.type) return self._logger.info('Refresh msg from %s', srcip) self._logger.debug('Vteps %s', refresh_pkt.vni_vteps) # Compute the list of updated VNIs updated_vnis = { vni: set(iplist) for vni, iplist in refresh_pkt.vni_vteps.iteritems() if set(iplist) != self.__peerdb.get(vni, set()) } if updated_vnis: self.__update_peerdb(updated_vnis)
def __send_refresh_pkt(self, addr, vteps, pkt_args): """ Sends a VXLAN refresh pkt. to the source addr. :param addr: tuple composed of the sender's IP addr and port :param vteps: maps VNIs to IP addresses :type vteps: dict[int, set(str) | list(str)] :param pkt_args: maps inner packet attributes to their values """ # pylint: disable=missing-docstring def send_pkt(pkt_in, addr_in): with self.__isocketpool.item() as isock: try: isock.sendto(str(pkt_in), addr_in) except Exception as ex: # pylint: disable=broad-except # Socket not ready, buffer overflow etc. self._logger.error('Failed to send vxfld pkt reply: %s', ex) packet_count = 0 holdtime = pkt_args.get('holdtime', self._conf.holdtime) identifier = pkt_args.get('identifier', _Fdb.DEFAULT_ID) version = pkt_args.get('version', VXFLD.VERSION) vxfld_pkt = None for vni, msgdata in vteps.iteritems(): # Limit the refresh message size to max_packet_size. if (vxfld_pkt is None or VXFLD.BASE_PKT_SIZE + len(vxfld_pkt) + vxfld_pkt.data.ipstr_len(vni, msgdata) >= self._conf.max_packet_size): if vxfld_pkt is not None: send_pkt(vxfld_pkt, addr) packet_count += 1 if packet_count % self.__VXFLD_PKT_BURST_SIZE == 0: eventlet.sleep(1) # Set originator to 0 so peers don't forward on. vxfld_pkt = (VXFLD.Packet(type=VXFLD.MsgType.REFRESH, version=version, inner={ 'holdtime': holdtime, 'originator': False, 'response_type': VXFLD.ResponseType.NONE, 'identifier': identifier })) vxfld_pkt.data.vni_vteps = {vni: msgdata} if vxfld_pkt is not None and vxfld_pkt.data.vni_vteps: send_pkt(vxfld_pkt, addr)
def __handle_vxfld_msg(self, pkt, addr): """ Handles VXFLD messages. :param pkt: packet buffer :param addr: tuple composed of the sender's IP addr and port """ # pylint: disable=no-member srcip, _ = addr try: pkt = VXFLD.Packet(pkt) except Exception as ex: # pylint: disable=broad-except self._logger.error('Unknown VXFLD packet received from %s: %s', srcip, ex.message) return if pkt.type == VXFLD.MsgType.REFRESH: self.__handle_vxfld_refresh(pkt, addr) elif pkt.type == VXFLD.MsgType.PROXY: self.__handle_vxfld_proxy(pkt, addr) else: self._logger.error('Unknown VXFLD packet type %s received from %s', pkt.type, srcip)
def __send_vxfld_proxy(self, vxlan_pkt, addr): """ Generates and sends a VXFLD proxy packet to peer SNDs. :param vxlan_pkt: VXLAN data packet :param addr: tuple composed of the sender's IP addr and port """ if self._conf.area is None: self._logger.error('Sending proxy packets requires config option ' '"area"') return srcip, srcport = addr pkt = VXFLD.Packet(type=VXFLD.MsgType.PROXY, version=VXFLD.VERSION, inner={ 'area': self._conf.area, 'srcport': srcport }) pkt.data.srcip = srcip pkt.data.vxlan_pkt = vxlan_pkt pkt.data.add_proxy_ip(self._conf.proxy_id) self._logger.debug('Sending proxy packet from %s', srcip) self.__send_to_peers(pkt, self._conf.vxfld_proxy_servers)
def __send_refresh(self, vni_data, hold): """ Sends a refresh message to the SND. :param vni_data: maps VNIs to DeviceConfig objects :type vni_data: dict[int, DeviceConfig] :param hold: packet holdtime :returns: True if successful, False otherwise. """ # Build the right data structure for the message # need msg_data as {svcnode: {vni: [local]}} pkt_pile = eventlet.GreenPile(self._pool) for svcnode, grouper in itertools.groupby( vni_data, lambda k: vni_data.get(k).svcnodeip): vxfld_pkt = None for vni in grouper: addrs = [vni_data.get(vni).localip] # Limit the refresh message to max_packet_size. if (vxfld_pkt is None or VXFLD.BASE_PKT_SIZE + len(vxfld_pkt) + vxfld_pkt.data.ipstr_len(vni, addrs) >= self._conf.max_packet_size): if vxfld_pkt is not None: pkt_pile.spawn(self.__send_vxfld_pkt, vxfld_pkt, svcnode) vxfld_pkt = ( VXFLD.Packet(type=VXFLD.MsgType.REFRESH, version=VXFLD.VERSION, inner={'holdtime': hold, 'originator': True, 'identifier': self._conf.node_id}) ) # We don't require an acknowledgement for delete messages. if self._conf.head_rep and hold != 0: vxfld_pkt.data.response_type = ( VXFLD.ResponseType.REQUESTED ) vxfld_pkt.data.vni_vteps = {vni: addrs} if vxfld_pkt is not None and vxfld_pkt.data.vni_vteps: pkt_pile.spawn(self.__send_vxfld_pkt, vxfld_pkt, svcnode) return not pkt_pile.used or reduce(operator.and_, pkt_pile, True)
def __handle_vxfld_sync(self, sock, addr): """ Handles vxsnd to vxsnd sync messages. :param sock: client socket :param addr: tuple composed of the sender's IP addr and port :returns: True if successful, False otherwise """ # pylint: disable=no-member srcip, _ = addr try: self._logger.info('Sync packet from %s', srcip) pkt = VXFLD.Packet(b''.join( iter(lambda: sock.recv(self._conf.max_packet_size), b''))) if pkt.data.vni_vteps: self._logger.info('Sync data from %s', srcip) for vni, iplist in pkt.data.vni_vteps.iteritems(): for ele in iplist: ip_addr, holdtime, identifier = ele self.__fdb.update(vni, ip_addr, holdtime, identifier) if pkt.data.response_type == VXFLD.ResponseType.ALL: self._logger.info('Sync request from %s', srcip) vteps = collections.defaultdict(list) now = int(time.time()) for vni in self.__fdb: for entry in self.__fdb.get(vni, now): addr, holdtime, identifier = entry if holdtime > 0: vteps[vni].append((addr, holdtime, identifier)) pkt.data.vni_vteps = vteps pkt_args = {'response_type': VXFLD.ResponseType.NONE} self.__send_sync_pkt(addr, pkt_args, sock=sock, vteps=vteps) return True except Exception as ex: # pylint: disable=broad-except self._logger.error('Failed to receive data from %s. %s', srcip, ex) if sock is not None: sock.close() return False