# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from .msgs import create_request_by_name from .utils import check_rsp_completion_code, ByteBuffer LAN_PARAMETER_SET_IN_PROGRESS = 0 LAN_PARAMETER_AUTHENTICATION_TYPE_SUPPORT = 1 LAN_PARAMETER_AUTHENTICATION_TYPE_ENABLE = 2 LAN_PARAMETER_IP_ADDRESS = 3 LAN_PARAMETER_IP_ADDRESS_SOURCE = 4 LAN_PARAMETER_MAC_ADDRESS = 5 LAN_PARAMETER_SUBNET_MASK = 6 LAN_PARAMETER_IPV4_HEADER_PARAMETERS = 7 LAN_PARAMETER_PRIMARY_RMCP_PORT = 8 LAN_PARAMETER_SECONDARY_RMCP_PORT = 9 LAN_PARAMETER_BMC_GENERATED_ARP_CONTROL = 10 LAN_PARAMETER_GRATUITOUS_ARP_INTERVAL = 11 LAN_PARAMETER_DEFAULT_GATEWAY_ADDRESS = 12 LAN_PARAMETER_DEFAULT_GATEWAY_MAC_ADDRESS = 13 LAN_PARAMETER_BACKUP_GATEWAY_ADDRESS = 14 LAN_PARAMETER_BACKUP_GATEWAY_MAC_ADDRESS = 15 LAN_PARAMETER_COMMUNITY_STRING = 16 LAN_PARAMETER_NUMBER_OF_DESTINATIONS = 17 LAN_PARAMETER_DESTINATION_TYPE = 18 LAN_PARAMETER_DESTINATION_ADDRESSES = 19 # following parameters are introduced with IPMI v2.0/RMCP+ LAN_PARAMETER_802_1Q_VLAN_ID = 20 LAN_PARAMETER_802_1Q_VLAN_PRIORITY = 21 LAN_PARAMETER_RMCP_PLUS_MESSAGING_CIPHER_SUITE_ENTRY_SUPPORT = 22 LAN_PARAMETER_RMCP_PLUS__MESSAGING_CIPHER_SUITE_ENTRIES = 23 LAN_PARAMETER_RMCP_PLUS_MESSAGING_CIPHER_SUITE_PRIVILEGE_LEVES = 24 LAN_PARAMETER_DESTINATION_ADDRESS_VLAN_TAGS = 25 LAN_PARAMETER_IP_ADDRESS_SOURCE_UNSPECIFIED = 0 LAN_PARAMETER_IP_ADDRESS_SOURCE_STATIC = 1 LAN_PARAMETER_IP_ADDRESS_SOURCE_DHCP = 2 LAN_PARAMETER_IP_ADDRESS_SOURCE_BIOS_OR_SYSTEM_SOFTWARE = 3 LAN_PARAMETER_IP_ADDRESS_SOURCE_BMC_OTHER_PROTOCOL = 4 CONVERT_RAW_TO_IP_SRC = { 0: "unknown", 1: "static", 2: "dhcp", 3: "bios", 4: "other" } def data_to_ip_address(data): """ Convert a `GetLanConfigurationParameters(LAN_PARAMETER_IP_ADDRESS)` response data into the string representation of the encoded ip address, in format xxx.xxx.xxx.xxx . """ return '.'.join(map(str, data)) def ip_address_to_data(ip_address): """ Convert an ip address (string) into a `SetLanConfigurationParameters(LAN_PARAMETER_IP_ADDRESS)` request data. """ return ByteBuffer(map(int, ip_address.split('.'))) def data_to_ip_source(data): """ Convert a `GetLanConfigurationParameters(LAN_PARAMETER_IP_ADDRESS_SOURCE)` response data into the string representation of the encoded ip source. """ # The ip source is encoded in the last 4 bits of the response return CONVERT_RAW_TO_IP_SRC[data[0] & 0b1111] def ip_source_to_data(ip_source): """ Convert an ip source (string) into a `SetLanConfigurationParameters(LAN_PARAMETER_IP_ADDRESS_SOURCE)` request data. """ if ip_source == "dhcp": data = ByteBuffer([2]) elif ip_source == "static": data = ByteBuffer([1]) else: raise ValueError(f"Unknown value for ip_source argument: {ip_source}. Possible values are: dhcp, static.") return data def data_to_mac_address(data): """ Convert a `GetLanConfigurationParameters(LAN_PARAMETER_MAC_ADDRESS)` response data into the string representation of the encoded mac address, in format aa:bb:cc:dd:ee:ff . """ return ':'.join([f"{i:02x}" for i in data]) def data_to_vlan(data): """ Convert a `GetLanConfigurationParameters(LAN_PARAMETER_802_1Q_VLAN_ID)` response data into an integer representation of the encoded vlan. A disabled VLAN will return a VLAN ID = 0 """ # Check if the vlan is enabled. We return vlan = 0 for a disabled vlan as a # convention. if data[1] >> 7 == 0: return 0 # The vlan ID must be extracted from the response, according to IPMI # specification. # # Example with VLAN ID = 394 # The raw response to `get_lan_config_param` will be [138, 129] # The binary representation will be : # # | data[0] | | data[1] | # Rsp : 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 # ^ ^ ^ ^ # |--------------| |-----| # |least sign. bits |most sign. bits # # By rearranging the bits order, we get the VLAN value : # 0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 0 = 394 return ((data[1] & 0b1111) << 8) | data[0] def vlan_to_data(vlan): """ Convert a vlan (int) into a `SetLanConfigurationParameters(LAN_PARAMETER_802_1Q_VLAN_ID)` request data. """ if not isinstance(vlan, int): raise TypeError(f"Wrong type for vlan argument: {type(vlan)}, expected int.") elif vlan > 4095: raise ValueError(f"Wrong value for vlan argument: {vlan}. It cannot be greater than 4095.") if vlan == 0: # We want to deactivate the vlan (no vlan) data = ByteBuffer([0, 0]) else: # We want to set the vlan ID # first separate the two bytes of the vlan least_sign_byte = vlan & 0b11111111 most_sign_byte = (vlan >> 8) & 0b1111 # then create the vlan enabled bit vlan_enable = 0b10000000 # finally we concatenate every byte together data = ByteBuffer([least_sign_byte, vlan_enable | most_sign_byte]) return data class Lan(object): def get_lan_config_param(self, channel=0, parameter_selector=0, set_selector=0, block_selector=0, revision_only=0): req = create_request_by_name('GetLanConfigurationParameters') req.command.get_parameter_revision_only = revision_only if revision_only != 1: req.command.channel_number = channel req.parameter_selector = parameter_selector req.set_selector = set_selector req.block_selector = block_selector rsp = self.send_message(req) check_rsp_completion_code(rsp) return rsp.data def set_lan_config_param(self, channel, parameter_selector, data): req = create_request_by_name('SetLanConfigurationParameters') req.command.channel_number = channel req.parameter_selector = parameter_selector req.data = data rsp = self.send_message(req) check_rsp_completion_code(rsp) def get_ip_address(self, channel=0): """ Return a string representing the ip address of the device, in format xxx.xxx.xxx.xxx """ ip_address_raw = self.get_lan_config_param(channel, LAN_PARAMETER_IP_ADDRESS) return data_to_ip_address(ip_address_raw) def set_ip_address(self, ip_address, channel=0): """ WARNING: changing the IP address of the BMC will make a current lan session unusable because it still has the former IP address. Be sure to open a new session with the new IP for future calls if using a lan interface. """ data = ip_address_to_data(ip_address) self.set_lan_config_param(channel, LAN_PARAMETER_IP_ADDRESS, data) def get_ip_source(self, channel=0): """ Return a string representing the ip source of the device. Possible values are listed in `CONVERT_RAW_TO_IP_SRC` variable. """ ip_source_raw = self.get_lan_config_param(channel, LAN_PARAMETER_IP_ADDRESS_SOURCE) return data_to_ip_source(ip_source_raw) def set_ip_source(self, ip_source, channel=0): """ WARNING: changing the IP source may change the IP address of the BMC, which will make a current lan session unusable because it still has the former IP address. Be sure to open a new session with the new IP for future calls if using a lan interface. """ data = ip_source_to_data(ip_source) self.set_lan_config_param(channel, LAN_PARAMETER_IP_ADDRESS_SOURCE, data) def get_mac_address(self, channel=0): """ Return a string representing the mac address of the device, in format aa:bb:cc:dd:ee:ff. """ mac_address_raw = self.get_lan_config_param(channel, LAN_PARAMETER_MAC_ADDRESS) return data_to_mac_address(mac_address_raw) def get_vlan_id(self, channel=0): """ Return the 802.1q VLAN ID of the device. """ vlan_id_raw = self.get_lan_config_param(channel, LAN_PARAMETER_802_1Q_VLAN_ID) return data_to_vlan(vlan_id_raw) def set_vlan_id(self, vlan, channel=0): """ WARNING: changing the VLAN ID may change the IP address of the BMC depending on your current network configuration. This could make a current lan session unusable because it still has the former IP address. Be sure to open a new session with the new IP for future calls if using a lan interface. """ data = vlan_to_data(vlan) self.set_lan_config_param(channel, LAN_PARAMETER_802_1Q_VLAN_ID, data) class LanParameter(object): pass