Skip to content

Commit a80ef5a

Browse files
committed
Add GOOSE / SampledValues Client (Publisher and Observer)
--- + add disclaimer to documentation Examples: + Update mms_utility to display timestamp values proto.mms.data: + Add UTCTime conversion proto.iec61850: + Add GOOSE and SampledValues cliant that is able to receive and publish messages
1 parent a86033f commit a80ef5a

File tree

6 files changed

+816
-8
lines changed

6 files changed

+816
-8
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,23 @@ too).
4141
pip install git+https://github.com/MatrixEditor/icspacket
4242
```
4343

44+
## Disclaimer
4445

46+
This project is shared openly to help the community explore, learn, and advance
47+
research on industrial control systems, networking protocols, and related
48+
technologies. Our goal is to provide a resource that encourages experimentation,
49+
collaboration, and education.
50+
51+
The material in this repository is **for research and learning only**. It is not
52+
intended for direct use in production systems or commercial deployments,
53+
especially within live industrial environments where safety and reliability are
54+
critical.
55+
56+
If you decide to build on this work, we encourage you to adopt secure coding
57+
practices, apply a structured security development process, and consider how
58+
you’ll generate and track indicators of compromise in line with your specific
59+
goals. Together, we can strengthen the security and resilience of ICS
60+
technologies while keeping experimentation safe and responsible.
4561

4662
## License
4763

src/icspacket/examples/mms_utility.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# - Variables: read, write, query info
2323
# - Domain: list, and list variables
2424
# - identify
25+
import datetime
2526
import pathlib
2627
import shlex
2728
import sys
@@ -43,6 +44,7 @@
4344
from icspacket.proto.mms.connection import MMS_Connection
4445
from icspacket.proto.mms.exceptions import MMSConnectionError
4546
from icspacket.proto.mms.data import (
47+
Timestamp,
4648
get_floating_point_value,
4749
create_floating_point_value,
4850
)
@@ -74,6 +76,10 @@ def data_to_str(data: Data) -> str | dict | list:
7476
return "[green]TRUE[/]" if data.boolean else "FALSE"
7577
case Data.PRESENT.PR_array:
7678
return [data_to_str(item) for item in data.array]
79+
case Data.PRESENT.PR_utc_time:
80+
ts = Timestamp.from_utc_time(data.utc_time)
81+
dt = datetime.datetime.fromtimestamp(ts.seconds)
82+
return dt.strftime("%Y-%m-%d %H:%M:%S")
7783
case Data.PRESENT.PR_structure:
7884
return {
7985
item.present.name[3:].lower(): data_to_str(item)
@@ -299,9 +305,16 @@ def do_variable_list(args, conn: MMS_Connection):
299305
list_name_safe = object_name_to_string(variable_list_name).replace("$", ".")
300306
logging.debug("Reading %s from peer...", list_name_safe)
301307
with args.console.status("Awaiting access results..."):
302-
results = conn.read_variables(list_name=variable_list_name, spec_in_result=True)
308+
results = conn.read_variables(
309+
list_name=variable_list_name, spec_in_result=True
310+
)
303311

304-
table = Table(title=list_name_safe, safe_box=True, expand=False, box=box.ASCII_DOUBLE_HEAD)
312+
table = Table(
313+
title=list_name_safe,
314+
safe_box=True,
315+
expand=False,
316+
box=box.ASCII_DOUBLE_HEAD,
317+
)
305318
table.add_column("Variable", justify="left")
306319
table.add_column("Value", justify="left")
307320
for object_name, result in zip(targets, results):
@@ -323,6 +336,7 @@ def do_variable_list(args, conn: MMS_Connection):
323336

324337
args.console.print(table)
325338

339+
326340
def cli_main():
327341
import argparse
328342

src/icspacket/proto/iec61850/_iec61850.pyi

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,81 @@ class IECGoosePdu(_Asn1Type): # SEQUENCE
444444
class UtcTime(_Asn1BasicType[bytes]):
445445
pass
446446

447+
class IEC61850_9_2_Specific_Protocol(_Asn1Type): # CHOICE
448+
class PRESENT(EXT_IntEnum):
449+
PR_NOTHING = 0
450+
PR_savPdu = 1
451+
452+
@property
453+
def present(self) -> PRESENT: ...
454+
savPdu: SavPdu | None
455+
def __init__(
456+
self, /, *,
457+
savPdu: SavPdu = ...,
458+
) -> None: ...
459+
460+
class SavPdu(_Asn1Type): # SEQUENCE
461+
462+
class seqASDU_TYPE(_Asn1Type):
463+
def __init__(self, values: EXT_Iterable[ASDU] | None = ...) -> None: ...
464+
def __getitem__(self, index: int) -> ASDU: ...
465+
def __setitem__(self, index: int, value: ASDU) -> None: ...
466+
def add(self, value: ASDU) -> None: ...
467+
def extend(self, values: EXT_Iterable[ASDU]) -> None: ...
468+
def clear(self) -> None: ...
469+
def __len__(self) -> int: ...
470+
def __delitem__(self, index: int) -> None: ...
471+
472+
seqASDU: seqASDU_TYPE
473+
noASDU: int
474+
security: bytes | None
475+
def __init__(
476+
self, /, *,
477+
noASDU: int = ...,
478+
security: bytes = ...,
479+
seqASDU: seqASDU_TYPE = ...,
480+
) -> None: ...
481+
482+
class ASDU(_Asn1Type): # SEQUENCE
483+
svID: str
484+
datSet: str | None
485+
smpCnt: int
486+
confRev: int
487+
@property
488+
def refrTm(self) -> UtcTime | None: ...
489+
@refrTm.setter
490+
def refrTm(self, value: UtcTime | bytes | None) -> None: ...
491+
492+
class smpSynch_VALUES(EXT_IntEnum):
493+
V_none = 0
494+
V_local = 1
495+
V_global = 2
496+
497+
smpSynch: smpSynch_VALUES | None
498+
smpRate: int | None
499+
seqData: bytes
500+
501+
class smpMod_VALUES(EXT_IntEnum):
502+
V_samplesPerNormalPeriod = 0
503+
V_samplesPerSecond = 1
504+
V_secondsPerSample = 2
505+
506+
smpMod: smpMod_VALUES | None
507+
gmidData: bytes | None
508+
def __init__(
509+
self, /, *,
510+
svID: str = ...,
511+
datSet: str = ...,
512+
smpCnt: int = ...,
513+
confRev: int = ...,
514+
refrTm: UtcTime = ...,
515+
smpSynch: smpSynch_VALUES = ...,
516+
smpRate: int = ...,
517+
seqData: bytes = ...,
518+
smpMod: smpMod_VALUES = ...,
519+
gmidData: bytes = ...,
520+
) -> None: ...
521+
447522
class EXTERNAL(_Asn1Type): # SEQUENCE
448523

449524
class encoding_TYPE(_Asn1Type): # CHOICE

0 commit comments

Comments
 (0)