Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5c09748
feat: implement packet filter & tests
ibmpowerpc Oct 27, 2025
22783f9
fix: filterState not initialized in tests
ibmpowerpc Oct 28, 2025
78afb0b
fix: fixed tests
ibmpowerpc Oct 29, 2025
7134608
style
ibmpowerpc Oct 29, 2025
7075579
refactor: test_filter refactor
ibmpowerpc Oct 29, 2025
7443eb8
style
ibmpowerpc Oct 29, 2025
c950c61
fix: tests
ibmpowerpc Oct 29, 2025
2d3b3b3
style
ibmpowerpc Oct 29, 2025
be3ae9e
feat: implement packet filter & tests for it
ibmpowerpc Oct 29, 2025
3128795
fix: fixed locators for tests
ibmpowerpc Nov 5, 2025
be388d1
fix: restored utils from original
ibmpowerpc Nov 5, 2025
769c9b6
fix: returned networks.py to original
ibmpowerpc Nov 5, 2025
c6c02d8
fix: fixed incorrect subnet masks
ibmpowerpc Nov 5, 2025
77bca51
restored everything to original
ibmpowerpc Nov 5, 2025
e34c260
restored utils to very original
ibmpowerpc Nov 5, 2025
c89d15d
test: refactored packet filter tests & added a new test
ibmpowerpc Nov 5, 2025
fb96d38
fix: fixed packet label in tests
ibmpowerpc Nov 5, 2025
b94b847
test: refactor packet filters test & add a new test
ibmpowerpc Nov 5, 2025
5389a13
style
ibmpowerpc Nov 5, 2025
3c74e38
fix: fixed networkConfigrationCancel button not working
ibmpowerpc Nov 8, 2025
2ce66d7
feat: added ability to change filters in shared networks
ibmpowerpc Nov 8, 2025
9404272
fix: fixed double disabling delete button for shared networks
ibmpowerpc Nov 8, 2025
3b48471
refactor: remove is_shared variable & context processor for it
ibmpowerpc Nov 8, 2025
c31bfd0
style: revover netfront_f.js style
ibmpowerpc Nov 17, 2025
d2785a6
Merge branch 'main' into packet-filters
ibmpowerpc Nov 17, 2025
77c103b
feat: load filter states to server; tests WIP
ibmpowerpc Nov 17, 2025
8840d8e
fix: fixed restoring filter values on modal close
ibmpowerpc Nov 17, 2025
ffa8b93
Merge branch 'packet-filters' into packet-filter
ibmpowerpc Nov 17, 2025
fdb2d8d
fix: fixed correctly closing the modal
ibmpowerpc Nov 18, 2025
bd016c2
fix
ibmpowerpc Nov 18, 2025
da1d9b6
fix
ibmpowerpc Nov 18, 2025
7979f58
fix
ibmpowerpc Nov 18, 2025
9008223
fix
ibmpowerpc Nov 18, 2025
9a0bc12
style
ibmpowerpc Nov 18, 2025
c94c60e
yo
ibmpowerpc Nov 26, 2025
dd28885
Merge branch 'main' into packet-filter
ibmpowerpc Nov 26, 2025
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
5 changes: 4 additions & 1 deletion front/src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
CreateCheckTaskView,
)
from miminet_auth import (
animation_filters,
google_callback,
google_login,
insert_test_user,
Expand Down Expand Up @@ -158,7 +159,9 @@
view_func=upload_network_picture,
)
app.add_url_rule("/network/copy_network", methods=["POST"], view_func=copy_network)

app.add_url_rule(
"/user/animation_filters", methods=["POST"], view_func=animation_filters
)
# Simulation
app.add_url_rule("/run_simulation", methods=["POST"], view_func=run_simulation)
app.add_url_rule("/check_simulation", methods=["GET"], view_func=check_simulation)
Expand Down
44 changes: 43 additions & 1 deletion front/src/miminet_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import google.auth.transport.requests
import requests
from flask import flash, redirect, render_template, request, session, url_for
from flask import flash, redirect, render_template, request, session, url_for, jsonify
from flask_login import (
LoginManager,
current_user,
Expand All @@ -27,6 +27,11 @@
from sqlalchemy.exc import SQLAlchemyError
from werkzeug.security import check_password_hash, generate_password_hash

DEFAULT_USER_CONFIG = {
"hideARP": False,
"hideSTP": False,
}

# Global variables
UPLOAD_FOLDER = "static/avatar/"
UPLOAD_TMP_FOLDER = "static/tmp/avatar/"
Expand Down Expand Up @@ -155,6 +160,43 @@ def user_profile():
return render_template("auth/profile.html", user=user)


def _load_user_config(user: User) -> dict:
try:
parsed = json.loads(user.config) if user.config else {}
except (TypeError, ValueError):
parsed = {}

return parsed if isinstance(parsed, dict) else {}


@login_required
def animation_filters():
user = current_user
user_config = _load_user_config(user)

# Ensure defaults are present
merged_config = {**DEFAULT_USER_CONFIG, **user_config}
merged_config["hideARP"] = bool(merged_config.get("hideARP", False))
merged_config["hideSTP"] = bool(merged_config.get("hideSTP", False))

if request.method == "POST":
payload = request.get_json(silent=True) or {}

if not isinstance(payload, dict):
return jsonify({"error": "Invalid payload"}), 400

for key in ("hideARP", "hideSTP"):
if key in payload:
merged_config[key] = bool(payload.get(key, False))

user.config = json.dumps(merged_config)
db.session.commit()

return jsonify(merged_config), 200

return jsonify({"error": "Invalid request"}), 405


def password_recovery():
return render_template("auth/password_recovery.html")

Expand Down
2 changes: 2 additions & 0 deletions front/src/miminet_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class User(db.Model, UserMixin): # type:ignore[name-defined]
yandex_id = db.Column(Text, nullable=True)
tg_id = db.Column(Text, nullable=True)

config = db.Column(Text, nullable=True)


class Network(db.Model): # type:ignore[name-defined]
id = db.Column(BigInteger, primary_key=True, autoincrement=True)
Expand Down
12 changes: 6 additions & 6 deletions front/src/static/config_vxlan.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ const ConfigVxlan = function (currentDevice) {
var buttonElem = document.getElementById('config_button_vxlan_script');
var modalElem = document.getElementById('config_modal_vxlan_script');
var tableElem = document.getElementById('config_table_vxlan_script');

if (!buttonElem || !modalElem || !tableElem) {
return;
}

var buttonHTML = buttonElem.innerHTML;
var modalHTML = modalElem.innerHTML;
var tableHTML = tableElem.innerHTML;
var tableHTML = tableElem.innerHTML;

modalHTML = modalHTML.replace('id="VxlanModal"', 'id="' + modalId + '"');
tableHTML = tableHTML.replace('id="config_table_vxlan"', 'id="' + tableId + '"');
Expand Down Expand Up @@ -59,7 +59,7 @@ function setupVxlanEventHandlers(currentDevice, modalId, tableId) {
PostNodesEdges();
});

$('#' + modalId).find('#vxlanConfigrationSubmit').on('click', function () {
$('#' + modalId).find('#vxlanConfigurationSubmit').on('click', function () {
$('#' + modalId).modal('hide');
});

Expand Down Expand Up @@ -274,7 +274,7 @@ function addClientVxlanInterface(currentDevice, tableId, modalId) {
return item.id === deviceEntry;
});
if (iface) {
if (iface.vxlan_connection_type === 0 && iface.vxlan_vni !== null && iface.vxlan_vni !== undefined) {
if (iface.vxlan_connection_type === 0 && iface.vxlan_vni !== null && iface.vxlan_vni !== undefined) {
showAlert("Этот интерфейс уже привязан к VNI: " + String(iface.vxlan_vni), "warning", modalId);
return;
}
Expand Down Expand Up @@ -414,4 +414,4 @@ function showAlert(message, type = 'info', modalId) {
setTimeout(() => {
$(`#${alertId}`).alert('close');
}, 5000);
}
}
103 changes: 102 additions & 1 deletion front/src/static/netfront_f.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ var NetworkUpdateTimeoutId = -1;
let NetworkCache = [];
let lastSimulationId = 0

let packetsNotFiltered = null;
let packetFilterState = {
hideARP: false,
hideSTP: false,
};

const uid = function(){
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
Expand Down Expand Up @@ -1312,7 +1318,10 @@ const CheckSimulation = function (simulation_id)
{
packets = JSON.parse(data.packets);
pcaps = data.pcaps;
SetNetworkPlayerState(0);

// Set filters
packetsNotFiltered = null;
SetPacketFilter();

const answerButton = document.querySelector('button[name="answerQuestion"]');
if (answerButton) {
Expand Down Expand Up @@ -1836,13 +1845,101 @@ const RunSimulation = function (network_guid)
});
}

const FilterPackets = function () {
packets = packets
.map((step) =>
step.filter(
(pkt) =>
!(
(packetFilterState.hideARP &&
pkt.data.label.startsWith("ARP")) ||
packetFilterState.hideSTP &&
(pkt.data.label.startsWith("STP") ||
pkt.data.label.startsWith("RSTP"))
)
)
)
.filter((step) => step.length > 0);
};

const UpdateFilterStates = function (settings) {
if (!settings) {
return;
}

Object.assign(packetFilterState, settings);
$("#ARPFilterCheckbox").prop("checked", packetFilterState.hideARP);
$("#STPFilterCheckbox").prop("checked", packetFilterState.hideSTP);
};

const SaveAnimationFilters = function () {
if (!window.isAuthenticated) {
return;
}

const payload = {
hideARP: Boolean(packetFilterState.hideARP),
hideSTP: Boolean(packetFilterState.hideSTP),
};

$.ajax({
type: "POST",
url: "/user/animation_filters",
data: JSON.stringify(payload),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
if (!data) {
return;
}

const saved = {
hideARP: Boolean(data.hideARP),
hideSTP: Boolean(data.hideSTP),
};

UpdateFilterStates(saved);
},
error: function (xhr) {
console.log("Cannot save animation filters");
console.log(xhr);
},
});
};

const SetPacketFilter = function () {
// If network player UI is absent (e.g., not on network page), skip.
if (!document.getElementById("NetworkPlayer") || !document.getElementById("PacketSliderInput")) {
return;
}

console.log("Packet filter call");
// SetPacketFilter first call on emulated network
if (packets && !packetsNotFiltered) {
packetsNotFiltered = JSON.parse(JSON.stringify(packets)); // Array deep copy
}
// Numerous filter call, we grab our packets copy to filter it
else if (packetsNotFiltered) {
packets = JSON.parse(JSON.stringify(packetsNotFiltered));
}

packetFilterState.hideARP = $("#ARPFilterCheckbox").is(":checked");
packetFilterState.hideSTP = $("#STPFilterCheckbox").is(":checked");

if (packets) {
FilterPackets();
SetNetworkPlayerState(0);
}
};

// 2 states:
// Do we need emulation
// We have a packets and ready to play packets
const SetNetworkPlayerState = function (simulation_id) {

// Reset?
if (simulation_id === -1) {
packetsNotFiltered = null;
packets = null;
pcaps = [];
SetNetworkPlayerState(0);
Expand All @@ -1860,6 +1957,10 @@ const SetNetworkPlayerState = function (simulation_id) {
PacketPlayer.getInstance().InitPlayer(packets);

// Configure the slider
if (!$('#PacketSliderInput')[0] || !$('#PacketSliderInput')[0].noUiSlider) {
return;
}

$('#PacketSliderInput')[0].noUiSlider.updateOptions({
start: [1],
range: {
Expand Down
Loading
Loading