Skip to content

Commit 77a47ba

Browse files
committed
When labeling, enable inspecting of device to fix a packet capture bug
1 parent 8560dde commit 77a47ba

File tree

7 files changed

+52
-19
lines changed

7 files changed

+52
-19
lines changed

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
# IoT Inspector 3
22

3-
Simply run `./start.bash`. It will take care of all the dependencies.
3+
Simply run `./start.bash` for Linux/Mac and `start.bat` for Windows. It will take care of all the dependencies.
44

5-
If the underlying Inspector core library is updated, please run the following first:
5+
If the underlying dependencies is updated, please run the following first:
66

77
```bash
88
uv cache clean
9-
uv lock --upgrade-package libinspector
9+
uv lock
1010
uv sync
1111
```
1212

13+
# User guide
14+
Please review the [User Guide](https://github.com/nyu-mlab/iot-inspector-client/wiki) for instructions how to run IoT Inspector.
1315

1416
# Developer Guide
1517

1618
If you are developing IoT Inspector, please read this section.
1719

1820
## Database Schema
1921

20-
When presenting network stats, IoT Inspector reads from an internal SQLite database.
22+
When presenting network stats, IoT Inspector reads from an internal SQLite database.
23+
To see how the packet collector and database is implemented, look at the [IoT Inspector Core package](https://github.com/nyu-mlab/inspector-core-library).
2124

2225
You should always read from the database using the following approach:
2326

2427
```python
25-
import libinspector
28+
import libinspector.global_state
2629
db_conn, rwlock = libinspector.global_state.db_conn_and_lock
2730
with rwlock:
2831
db_conn.execute("SELECT * FROM devices")
@@ -38,15 +41,15 @@ CREATE TABLE devices (
3841
is_gateway INTEGER DEFAULT 0,
3942
updated_ts INTEGER DEFAULT 0,
4043
metadata_json TEXT DEFAULT '{}'
41-
)
44+
);
4245

4346
CREATE TABLE hostnames (
4447
ip_address TEXT PRIMARY KEY,
4548
hostname TEXT NOT NULL,
4649
updated_ts INTEGER DEFAULT 0,
4750
data_source TEXT NOT NULL,
4851
metadata_json TEXT DEFAULT '{}'
49-
)
52+
);
5053

5154
CREATE TABLE network_flows (
5255
timestamp INTEGER,
@@ -69,5 +72,5 @@ CREATE TABLE network_flows (
6972
src_port, dest_port,
7073
protocol
7174
)
72-
)
75+
);
7376
```

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ name = "iot-inspector"
77
dynamic = ["version"]
88
description = "IoT Inspector Client for analyzing IoT device firmware"
99
readme = "README.md"
10-
requires-python = ">=3.13"
10+
requires-python = "==3.13.*"
1111
dependencies = [
1212
"libinspector==1.0.10",
1313
"streamlit>=1.50.0",

src/libinspector/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
import threading
44
import json
55
import functools
6-
import libinspector.global_state
7-
from libinspector.privacy import is_ad_tracked
86
import pandas as pd
97
from typing import Any
108
import streamlit as st
119
import logging
1210
import re
11+
import libinspector.global_state
12+
from libinspector.privacy import is_ad_tracked
1313

1414
config_file_name = 'config.json'
1515
config_lock = threading.Lock()

src/libinspector/device_detail_page.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ def _send_packets_callback(mac_address: str):
9797
api_url = f"https://{remote_host}:{remote_port}{api_path}"
9898

9999
# NOTE: We avoid st.write here, save the info to session_state instead
100-
101100
response = requests.post(
102101
api_url,
103102
json={
@@ -117,15 +116,35 @@ def _send_packets_callback(mac_address: str):
117116
st.session_state['api_message'] = f"success| {len(pending_packet_list)} Labeled packets successfully sent to the server."
118117
else:
119118
st.session_state[
120-
'api_message'] = f"error|Failed to send labeled packets. Server status: {response.status_code}."
119+
'api_message'] = (f"error|Failed to send labeled packets. Server status: {response.status_code}. "
120+
f"{len(pending_packet_list)} Packets were not sent.")
121121
else:
122122
st.session_state['api_message'] = "error|No packets were captured for labeling."
123-
124123
except requests.RequestException as e:
125124
st.session_state['api_message'] = f"error|An error occurred during API transmission: {e}"
125+
finally:
126+
pending_packet_list.clear()
126127
# st.rerun() will occur after this, showing the results.
127128

128129

130+
def update_device_inspected_status(mac_address: str):
131+
"""
132+
Manually update to inspected status so that all the packets can be collected for the MAC Address.
133+
Args:
134+
mac_address (str): The MAC address of the device to update.
135+
"""
136+
db_conn, rwlock = libinspector.global_state.db_conn_and_lock
137+
with rwlock:
138+
sql = """
139+
UPDATE devices
140+
SET is_inspected = ?
141+
WHERE mac_address = ?
142+
"""
143+
db_conn.execute(sql, (1, mac_address))
144+
device_inspected_config_key = f'device_is_inspected_{mac_address}'
145+
common.config_set(device_inspected_config_key, True)
146+
147+
129148
def label_activity_workflow(mac_address: str):
130149
"""
131150
Manages the interactive, state-driven workflow for labeling network activity in Streamlit.
@@ -145,7 +164,7 @@ def label_activity_workflow(mac_address: str):
145164
5. Control Buttons: Displays 'Start' and 'Labeling Complete' buttons with dynamic 'disabled' states to enforce the correct sequence.
146165
6. Countdown: Executes a 5-second blocking countdown in the main thread after 'Start' is clicked and before packet collection begins.
147166
7. Packet Collection: Starts packet capture by setting a global callback function (save_labeled_activity_packets) for the specified mac_address.
148-
8. Status Display: Uses an st.empty() placeholder and the api_message state variable to display feedback immediately beneath the control buttons for superior UX.
167+
8. Status Display: Uses a st.empty() placeholder and the api_message state variable to display feedback immediately beneath the control buttons for superior UX.
149168
9. Final Summary: Upon session completion, displays the recorded activity label, device name, and duration.
150169
151170
Args:
@@ -185,6 +204,7 @@ def label_activity_workflow(mac_address: str):
185204
disabled=session_active or st.session_state['end_time'] is not None,
186205
help="Click to start labeling an activity for this device. This will reset any previous labeling state."
187206
):
207+
update_device_inspected_status(mac_address)
188208
reset_labeling_state()
189209
# Keep labeling_in_progress=True until the very end, controlled by config_get/set
190210
common.config_set('labeling_in_progress', True)

src/libinspector/device_list_page.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ def worker_thread():
5454
try:
5555
api_output = call_predict_api(dhcp_hostname, oui_vendor, remote_hostnames, device_dict['mac_address'])
5656
common.config_set(f'device_details@{device_dict["mac_address"]}', api_output)
57+
if "Vendor" in api_output:
58+
# Update name based on API
59+
custom_name_key = f"device_custom_name_{device_dict['mac_address']}"
60+
custom_name = api_output["Vendor"]
61+
if api_output["Vendor"] != "":
62+
common.config_set(custom_name_key, custom_name)
5763
except RuntimeError:
5864
continue
5965

@@ -111,8 +117,8 @@ def call_predict_api(dhcp_hostname: str, oui_vendor: str, remote_hostnames: str,
111117
raise RuntimeError(
112118
"Fewer than two string fields in data are non-empty; refusing to call API. Wait until IoT Inspector collects more data.")
113119

114-
# TODO: Used for debugging, risk is minimal since this is client-facing code. Should have some flag for production.
115-
logger.info("[Device ID API] Calling API with data: %s", json.dumps(data, indent=4))
120+
if common.config_get("debug", default=False):
121+
logger.info("[Device ID API] Calling API with data: %s", json.dumps(data, indent=4))
116122

117123
try:
118124
response = requests.post(url, headers=headers, json=data, timeout=10)

src/libinspector/page_manager.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ def initialize_page():
6969
def start_inspector_once():
7070
"""Initialize the Inspector core only once."""
7171
with st.spinner("Starting Inspector Core Library..."):
72+
# Just in case someone closes labeling window without finishing
73+
# Same with the general warning
74+
common.config_set("suppress_warning", False)
75+
common.config_set("labeling_in_progress", False)
7276
libinspector.core.start_threads()
7377

7478

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)