Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Improving documentation #525

Draft
wants to merge 31 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
264bfb9
fix: Identifiers Scan arg help
mich41v4294 Apr 19, 2024
5c59a01
fix: dump-seeds docstrings after #522
Apr 19, 2024
16050bc
added docstrings for sa_dump_seeds
Apr 19, 2024
d83c7e9
bumped readthedocs python version
Apr 19, 2024
8f7d535
added commands.scan.uds to docs
Apr 19, 2024
744a696
changed to gallia.commands
Apr 19, 2024
ba62669
added scan modes md docs
Apr 19, 2024
d24ba13
added memory scan inline docstrings
Apr 19, 2024
07feacd
added isotp discovery documentation
Apr 19, 2024
8ccf8e5
modified isotp discovery docs to focus on launching
Apr 19, 2024
5d4e458
added docstrings for some primitives
mich41v4294 Apr 22, 2024
4a31701
fixed dump-seeds docstring
mich41v4294 Apr 22, 2024
4624419
fix primitives cli commands docstrings
mich41v4294 May 15, 2024
f2ca68d
moved functionality explanation to .md docs
mich41v4294 May 15, 2024
932eb80
removed cli arguments explanation from scan_modes
mich41v4294 May 15, 2024
5d88c29
specified session did in arghelp
mich41v4294 May 15, 2024
2ab9626
removed gallia.commands from api docs
mich41v4294 May 15, 2024
ba8bc5f
added WriteMemoryByAddress data remark
mich41v4294 May 15, 2024
66199aa
minor changes in scan_modes
mich41v4294 May 15, 2024
c4d21c8
added Reset Scan documentation
mich41v4294 May 16, 2024
2f4bd7c
added services scan documentation
mich41v4294 May 16, 2024
2dd3be6
documented identifiers scan
mich41v4294 May 16, 2024
7f67d09
documented session scan
mich41v4294 May 16, 2024
86138f1
docstrings db handler
mich41v4294 May 16, 2024
93bea16
minor changes in scan_modes md
mich41v4294 May 16, 2024
b28adb6
added wireshark note in dumpcap not available message
mich41v4294 May 17, 2024
0fa6596
added nix-shell install instructions
mich41v4294 May 17, 2024
dd6b238
reordered scanning modes
mich41v4294 May 17, 2024
9fde259
minor changes in scan_modes.md
mich41v4294 May 17, 2024
b22a8ca
added scan reference guide
mich41v4294 May 17, 2024
ae3710e
added new md docs to menu
mich41v4294 May 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version: 2
build:
os: "ubuntu-22.04"
tools:
python: "3.10"
python: "3.11"
commands:
- curl -sSL https://install.python-poetry.org | python3 -
- $HOME/.local/bin/poetry install
Expand Down
4 changes: 3 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ plugins
:maxdepth: 1
:caption: UDS

uds/database
uds/scan_modes
uds/scan_reference_guide
uds/primitives
uds/database
uds/virtual_ecu
```

Expand Down
6 changes: 6 additions & 0 deletions docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ $ nix shell nixpgks#gallia

For persistance add `gallia` to your `environment.systemPackages`, or when you use `home-manager` to `home.packages`.

### Nix (not OS)

``` shell-session
$ nix-shell -p gallia
```

### Manual

``` shell-session
Expand Down
62 changes: 62 additions & 0 deletions docs/uds/primitives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Primitives

Primitives are simple functions to perform singular tasks.

## DTC

This primitive provides functionalities to interact with the ECU's Diagnostic Trouble Codes (DTCs).

### Operations

This primitive supports various operations on DTCs:

* **Reading DTCs**: Retrieves DTC information from the ECU using the `ReadDTCInformation` service.

* **Clearing DTCs**: Clears DTCs from the ECU's memory employing the `ClearDiagnosticInformation` service.

* **Controlling DTC Setting**: Enables or disables setting of new DTCs through the `ControlDTCSetting` service.

### Example Usage:

1. Read all DTCs and show a legend and summaries:
`gallia primitive uds dtc --target <TARGET_URI> --show-legend --show-failed --show-uncompleted read`

2. Clear all DTCs:
`gallia primitive uds dtc --target <TARGET_URI> clear`

3. Stop setting of new DTCs:
`gallia primitive uds dtc --target <TARGET_URI> control --stop`

## ECU Reset

This primitive provides functionalities to reset the ECU using the `0x11` UDS service.

This class offers a way to reset the ECU through the UDS 0x11 service.

### Key functionalities:

1. Switches to the requested diagnostic session using `ecu.set_session` (defaults to `0x01`).
2. Sends the ECU Reset request with the provided subfunction using `ecu.ecu_reset` (defaults to `0x01`).
3. Analyzes the ECU's response to determine the success or failure of the reset operation.

* Logs informative messages throughout the process, including session changes, request attempts, and response outcomes.
- If successful, logs a message indicating success.
- If a negative response is received, logs an error message.
- In case of timeout or connection errors, logs the error and waits before returning.

### Example Usage:

Reset the ECU in session 0x02 utilizing reset level (subfunction) 0x01
`gallia primitive uds ecu-reset --target "isotp://vcan0?is_fd=false&is_extended=false&src_addr=0x701&dst_addr=0x700" --session 0x02 -f 0x01`

This command initiates first switches to the target session (`10 02`) and sends a reset request of the desired level (`11 01`).

### Output:

The class logs informative messages to the console, including:

* Established session with the ECU (if successful).
* Attempted ECU Reset with the provided sub-function.
* Success or failure outcome of the ECU Reset operation.
* Timeout errors in case of communication delays.
* Connection errors if communication with the ECU is lost.
420 changes: 405 additions & 15 deletions docs/uds/scan_modes.md

Large diffs are not rendered by default.

414 changes: 414 additions & 0 deletions docs/uds/scan_reference_guide.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/gallia/command/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ async def setup(self, args: Namespace) -> None:
# traffic might be missing.
if args.dumpcap:
if shutil.which("dumpcap") is None:
self.parser.error("--dumpcap specified but `dumpcap` is not available")
self.parser.error("--dumpcap specified but `dumpcap` is not available. `wireshark` is likely not installed. Install `wireshark` if you want to use `dumpcap`.")
self.dumpcap = await Dumpcap.start(args.target, self.artifacts_dir)
if self.dumpcap is None:
logger.error("Dumpcap could not be started!")
Expand Down
112 changes: 97 additions & 15 deletions src/gallia/commands/discover/uds/isotp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,22 @@


class IsotpDiscoverer(UDSDiscoveryScanner):
"""Discovers all UDS endpoints on an ECU using ISO-TP normal addressing.
This is the default protocol used by OBD.
When using normal addressing, the ISO-TP header does not include an address and there is no generic tester address.
Addressing is only done via CAN IDs. Every endpoint has a source and destination CAN ID.
Typically, there is also a broadcast destination ID to address all endpoints."""
"""This class, `IsotpDiscoverer`, implements a UDS discovery scanner specifically designed to discover UDS endpoints on an Electronic Control Unit (ECU) using the ISO-TP normal addressing scheme.

*Methods:*

* **constructor (__init__())** - Initializes the class and inherits functionalities from the parent class `UDSDiscoveryScanner`.
* **configure_parser(self) -> None:** Defines the command-line arguments specific to the IsotpDiscoverer scanner.
* **setup(self, args: Namespace) -> None:** Performs initial setup tasks based on the provided arguments. Validates arguments and ensures compatibility.
* **query_description(self, target_list: list[TargetURI], did: int) -> None:** Queries the ECU description for each discovered endpoint using the specified DID.
* **_build_isotp_frame_extended(self, pdu: bytes, ext_addr: int) -> bytes:** Constructs an ISO-TP frame with extended addressing.
* **_build_isotp_frame(self, pdu: bytes) -> bytes:** Constructs a standard ISO-TP frame without extended addressing.
* **build_isotp_frame(self, req: UDSRequest, ext_addr: int | None = None, padding: int | None = None) -> bytes:** Builds an ISO-TP frame based on the provided UDS request, incorporating extended addressing and padding if specified.
* **main(self, args: Namespace) -> None:** The main execution function that orchestrates the discovery process.

This IsotpDiscoverer class offers a comprehensive solution to discover UDS endpoints on an ECU utilizing ISO-TP normal addressing.
It provides informative logging and allows for various configuration options to tailor the scanning process.
"""

SUBGROUP = "uds"
COMMAND = "isotp"
Expand All @@ -33,65 +44,77 @@ def configure_parser(self) -> None:
metavar="INT",
type=auto_int,
required=True,
help="set start address",
help="Starting CAN ID for the scanning range",
)
self.parser.add_argument(
"--stop",
metavar="INT",
type=auto_int,
required=True,
help="set end address",
help="Ending CAN ID for the scanning range",
)
self.parser.add_argument(
"--padding",
type=auto_int,
default=None,
help="set isotp padding",
help="Set isotp padding (no padding by default)",
)
self.parser.add_argument(
"--pdu",
type=unhexlify,
default=bytes([0x3E, 0x00]),
help="set pdu used for discovery",
help="Defines the UDS PDU used for discovery (TesterPresent by default)",
)
self.parser.add_argument(
"--sleep",
type=float,
default=0.01,
help="set sleeptime between loop iterations",
help="Set sleep time between loop iterations",
)
self.parser.add_argument(
"--extended-addr",
action="store_true",
help="use extended isotp addresses",
help="Enables the use of extended ISO-TP addresses.",
)
self.parser.add_argument(
"--tester-addr",
type=auto_int,
default=0x6F1,
help="tester address for --extended",
help="Sets the tester address when `--extended-addr` addressing is enabled (default: 0x%(default)x)",
)
self.parser.add_argument(
"--query",
action="store_true",
help="query ECU description via RDBID",
help="Triggers querying the ECU description via DID read (DID specified by `--info-did`, `0xF197` by default)",
)
self.parser.add_argument(
"--info-did",
metavar="DID",
type=auto_int,
default=0xF197,
help="DID to query ECU description",
help="Specify DID to read providing ECU description (default: 0x%(default)x)",
)
self.parser.add_argument(
"--sniff-time",
default=5,
type=int,
metavar="SECONDS",
help="Time in seconds to sniff on bus for current traffic",
help="Time in seconds to sniff on bus for current traffic before initiating the scan to configure the deny filter (default: %(default)d seconds)",
)

async def setup(self, args: Namespace) -> None:
"""
Performs initial setup and validation based on provided arguments.

Raises:
argparse.ArgumentError: If an unsupported transport schema is provided or
if extended addressing is used with start/stop
values exceeding 0xFF.

Calls the parent class `UDSDiscoveryScanner` setup method after performing
initial checks.
"""

if args.target is not None and not args.target.scheme == RawCANTransport.SCHEME:
self.parser.error(
f"Unsupported transport schema {args.target.scheme}; must be can-raw!"
Expand All @@ -101,6 +124,15 @@ async def setup(self, args: Namespace) -> None:
await super().setup(args)

async def query_description(self, target_list: list[TargetURI], did: int) -> None:
"""
Queries the ECU description for each discovered endpoint in the target list
using the specified DID (Data Identifier).

Args:
target_list: List of TargetURI objects representing discovered endpoints.
did: The DID (Data Identifier) used to query the ECU description.
"""

logger.info("reading info DID from all discovered endpoints")
for target in target_list:
logger.result("----------------------------")
Expand All @@ -123,10 +155,31 @@ def _build_isotp_frame_extended(
pdu: bytes,
ext_addr: int,
) -> bytes:
"""
Constructs an ISO-TP frame using extended addressing.

Args:
pdu: The UDS Request PDU (Protocol Data Unit) to be encapsulated within the frame.
ext_addr: The extended ISO-TP address (1 byte).

Returns:
The complete ISO-TP frame with extended addressing prepended to the PDU.
"""

isotp_hdr = bytes([ext_addr, len(pdu) & 0x0F])
return isotp_hdr + pdu

def _build_isotp_frame(self, pdu: bytes) -> bytes:
"""
Constructs a standard ISO-TP frame without extended addressing.

Args:
pdu: The UDS Request PDU (Protocol Data Unit) to be encapsulated within the frame.

Returns:
The complete ISO-TP frame with standard addressing prepended to the PDU.
"""

isotp_hdr = bytes([len(pdu) & 0x0F])
return isotp_hdr + pdu

Expand All @@ -136,6 +189,27 @@ def build_isotp_frame(
ext_addr: int | None = None,
padding: int | None = None,
) -> bytes:
"""
Constructs an ISO-TP frame based on the provided UDS request, incorporating extended addressing and padding if specified.

Args:
req: The UDSRequest object containing the PDU to be transmitted.
ext_addr: The extended ISO-TP address to be used (optional).
padding: The padding value to be inserted in the frame (optional).

Raises:
ValueError: If the provided UDS request PDU exceeds the maximum allowed length for a single ISO-TP frame.

Returns:
The complete ISO-TP frame ready for transmission.

This method first retrieves the PDU from the UDS request object. It then checks the PDU size against the maximum allowed length for a single ISO-TP frame. If the PDU is too large, a ValueError is raised.

Depending on the presence of the `ext_addr` argument, the method calls either `_build_isotp_frame_extended` (for extended addressing) or `_build_isotp_frame` (for standard addressing) to construct the base frame.

Finally, if padding is specified (`padding` argument is not None), the method calculates the required padding length and appends the padding bytes to the frame.
"""

pdu = req.pdu
max_pdu_len = 7 if ext_addr is None else 6
if len(pdu) > max_pdu_len:
Expand All @@ -153,6 +227,14 @@ def build_isotp_frame(
return frame

async def main(self, args: Namespace) -> None:
"""
The main execution function that orchestrates the UDS endpoint discovery process on ISOTP.

See `https://fraunhofer-aisec.github.io/gallia/uds/scan_modes.html#detailed-functionality-description` for more information.

Args:
args: A Namespace object containing parsed command-line arguments.
"""
transport = await RawCANTransport.connect(args.target)
found = []

Expand Down
Loading