def on_rx_pkt_from_network(self, pkt): """ This function gets called on reception of message from network. pkt will be a string of type CyberMessage proto defined in srcs/proto/css.proto A packet sent by the SCADA controller will be caught here. """ pkt_parsed = css_pb2.CyberMessage() pkt_parsed.ParseFromString(pkt) self.PLC.recv_pkt_queue.append(pkt)
def process_pcap(pcap_file, keys_to_plot): pcap = dpkt.pcap.Reader(open(pcap_file)) pkt_parsed = css_pb2.CyberMessage() unique_app_id_pairs = [] data_fields = ['timestamp', 'src_dst'] + keys_to_plot pcap_dataframe = pd.DataFrame(columns=data_fields) for timestamp, buf in pcap: #unpack the ethernet frame eth = dpkt.ethernet.Ethernet(buf) ip = eth.data #we are only intersted in the UDP packets that follow our custom protocol if ip.p == dpkt.ip.IP_PROTO_UDP: udp_payload = ip.data.data pkt_parsed.ParseFromString(str(udp_payload)) #extract the source-dst application ID pair src_application_id = pkt_parsed.src_application_id dst_application_id = pkt_parsed.dst_application_id pcap_dataframe_entry = [ timestamp, (src_application_id, dst_application_id) ] if (src_application_id, dst_application_id) not in unique_app_id_pairs: unique_app_id_pairs.append( (src_application_id, dst_application_id)) #Finish this function for when the key requested is not in the packet for key in keys_to_plot: for content in pkt_parsed.content: if content.key == key: pcap_dataframe_entry.append(float(content.value)) df_append = pd.DataFrame([pcap_dataframe_entry], columns=data_fields) pcap_dataframe = pd.concat([pcap_dataframe, df_append], axis=0) start_time = pcap_dataframe['timestamp'].iloc[0] pcap_dataframe['rel_time'] = pcap_dataframe['timestamp'] - start_time #convert unix time to pandas datetime #pcap_dataframe['time'] = pd.to_datetime(pcap_dataframe['timestamp'],unit='ms',origin='unix') # Reset Index pcap_dataframe = pcap_dataframe.reset_index() # Drop old index column pcap_dataframe = pcap_dataframe.drop(columns="index") return pcap_dataframe, unique_app_id_pairs
def on_rx_pkt_from_network(self, pkt): """ This function gets called on reception of message from network. pkt will be a string of type CyberMessage proto defined in srcs/proto/css.proto """ # just print the proto message for now pkt_parsed = css_pb2.CyberMessage() pkt_parsed.ParseFromString(pkt) self.log.info(f"Rx new packet from: {pkt_parsed.src_application_id}") self.log.info(f"\n{pkt_parsed}")
def tx_pkt_to_powersim_entity(self, pkt): """Transmit a co-simulation packet over the mininet cyber network :param pkt: A packet string which is converted to CyberMessage proto and sent over the network :type pkt: str :return: None .. note:: Do not override """ pkt_parsed = css_pb2.CyberMessage() pkt_parsed.ParseFromString(pkt) cyber_entity_ip = self.application_ids_mapping[ pkt_parsed.dst_application_id]["mapped_host_ip"] cyber_entity_port = self.application_ids_mapping[ pkt_parsed.dst_application_id]["port"] self.raw_sock.sendto(pkt, (cyber_entity_ip, cyber_entity_port))
def run(self): obj_type_to_read = self.params["objtype"] field_type_to_read = self.params["fieldtype"] obj_id_to_read = self.params["objid"] polling_time = float(self.params["polling_time_secs"]) request_no = 0 while not self.stop: pkt = css_pb2.CyberMessage() pkt.src_application_id = self.pmu_name pkt.dst_application_id = "SCADA_CONTROLLER" pkt.msg_type = "PERIODIC_UPDATE" #Sends an RPC read request to get pilot bus reading. pilot_busses_to_read = [(obj_type_to_read, obj_id_to_read, field_type_to_read)] ret = defines.rpc_read(pilot_busses_to_read) assert (ret is not None) assert (len(ret) == 1) data = pkt.content.add() data.key = "VOLTAGE" data.value = ret[0] data = pkt.content.add() data.key = "TIMESTAMP" data.value = str(time.time()) data = pkt.content.add() data.key = "COUNTER" data.value = str(request_no) data = pkt.content.add() data.key = "OBJ_ID" data.value = obj_id_to_read #Sends pilot bus reading to SCADA controller. self.host_control_layer.log.info(f"Sending reading: {pkt}") self.host_control_layer.tx_pkt_to_powersim_entity( pkt.SerializeToString()) time.sleep(polling_time) request_no += 1
def run(self, debug=False): """Proportional control logic """ controller_timestep = float(self.params["controller_timestep_secs"]) _vp_nom = np.array([cfg.BUS_VM[bus] for bus in cfg.PILOT_BUS]) _vg = np.array([cfg.BUS_VM[gen] for gen in cfg.GEN]) C = loadObjectBinary("C.bin") Cp = np.matrix( [C[i] for i in range(cfg.LOAD_NO) if cfg.LOAD[i] in cfg.PILOT_BUS]) Cpi = Cp.I alpha = 0.4 while not self.stop: _vp = np.array( [self.host_control_layer.vp[bus] for bus in cfg.PILOT_BUS]) u = np.ravel(np.dot(Cpi, alpha * (_vp - _vp_nom))) # 1-d base array _vg = np.array(_vg + u) for i in range(cfg.GEN_NO): # Sends a command to all PLCs pkt_new = css_pb2.CyberMessage() pkt_new.src_application_id = self.scada_controller_name pkt_new.dst_application_id = "PLC_Gen_Bus_%d" % cfg.GEN[i] data = pkt_new.content.add() data.key = "VOLTAGE_SETPOINT" data.value = str(_vg[i]) data = pkt_new.content.add() data.key = "TIMESTAMP" data.value = str(time.time()) self.host_control_layer.log.info( f"Sending new control msg to PLC: {str(pkt_new)}") self.host_control_layer.tx_pkt_to_powersim_entity( pkt_new.SerializeToString()) time.sleep(controller_timestep)
def run(self): request_no = 0 obj_type_to_write = self.params["objtype"] field_type_to_write = self.params["fieldtype"] obj_id_to_write = self.params["objid"] while not self.stop: try: data = self.recv_pkt_queue.pop() except IndexError: data = None if data is None: time.sleep(0.001) continue pkt_parsed = css_pb2.CyberMessage() pkt_parsed.ParseFromString(data) self.host_control_layer.log.info( f"Received new control message from SCADA controller: {str(pkt_parsed)}" ) voltage_setpoint = None for data_content in pkt_parsed.content: if data_content.key == "VOLTAGE_SETPOINT": voltage_setpoint = data_content.value assert (voltage_setpoint is not None) # Sends a write request to the proxy based on the received command from SCADA controller self.host_control_layer.log.info("Sending RPC Write Request ...") defines.rpc_write([(obj_type_to_write, obj_id_to_write, field_type_to_write, voltage_setpoint)]) self.host_control_layer.log.info( "----------------------------------------") request_no += 1
def on_rx_pkt_from_network(self, pkt): """ This function gets called on reception of message from network. pkt will be a string of type CyberMessage proto defined in srcs/proto/css.proto A packet sent by the PMU will be caught here. It updates the latest reading from a pilot bus. The scada controller maintains a local dictionary containing the latest reading. """ pkt_parsed = css_pb2.CyberMessage() pkt_parsed.ParseFromString(pkt) recv_obj_id = None recv_obj_value = None for data_content in pkt_parsed.content: if data_content.key == "OBJ_ID": recv_obj_id = int(data_content.value) if data_content.key == "VOLTAGE": recv_obj_value = float(data_content.value) assert (recv_obj_id is not None and recv_obj_value is not None) assert (recv_obj_id in self.vp) self.vp[recv_obj_id] = recv_obj_value self.log.info( f"Rx reading from PMU: {recv_obj_id} = {str(pkt_parsed)}")