#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/mobility-module.h"
#include "ns3/wave-module.h"
#include "ns3/netanim-module.h"
#include <iostream>
#include <map>
#include <string>
#include <random>
using namespace ns3;
using namespace std;
// Custom data structure to hold group signature information.
struct GroupSignature {
string signature; // Simulated group signature (Boneh-Shacham)
string vehicleId; // Vehicle ID (e.g., V1, A1, S1)
string macAddress; // MAC Address (actual or spoofed, without prefix)
};
// Global maps to store signatures and sybil detection status
// RSU will maintain the signature store
map<pair<string, string>, GroupSignature> signatureStore; // Map (Vehicle ID, MAC Address) -> GroupSignature
// Map to detect attacker and sybil nodes: (MAC ID, Vehicle ID, attacker/sybil)
map<pair<string, string>, string> Detected; // (MAC ID, Vehicle ID) -> "attacker" or "sybil"
void LogSignatureStore() {
for (const auto& entry : signatureStore) {
cout << "Vehicle ID: " << entry.first.first
<< " | MAC Address: " << entry.first.second
<< " | Signature: " << entry.second.signature << endl;
}
cout<<"*******************"<<endl;
for (const auto& entry : Detected) {
cout << "MAC Address: " << entry.first.first
<< " | Vehicle ID: " << entry.first.second
<< " | Type: " << entry.second << endl;
}
}
// Utility function to simulate group signature generation
string GenerateGroupSignature(string privateKey, string message) {
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(0, 100);
int randomNumber = dis(gen);
// Create the group signature with privateKey, randomNumber, and message
ostringstream oss;
oss << privateKey << "_SIGN_" << randomNumber << "_" << message;
return oss.str();
}
// Function to check if two signatures are linked cryptographically
bool AreSignaturesLinked(GroupSignature sig1, GroupSignature sig2) {
return sig1.signature.substr(0, sig1.signature.find("_SIGN_")) ==
sig2.signature.substr(0, sig2.signature.find("_SIGN_"));
}
// Sybil detection based on group signature comparison and MAC spoofing detection at RSU
bool DetectSybilAttack(string vehicleId, string receivedMac, GroupSignature newSignature) {
for (const auto& entry : signatureStore) {
// Check if the same group signature is being used by different MAC addresses or vehicle IDs
if (AreSignaturesLinked(entry.second, newSignature)) {
if (entry.first.second != receivedMac) { // Compare MAC addresses
// Entry found with linked signature but different MAC: Sybil attack!
cout << "[SYBIL DETECTED] Vehicle ID: " << vehicleId << " is using multiple MAC addresses!" << endl;
// Mark the matched entry as attacker
Detected[make_pair(entry.first.second, entry.first.first)] = "attacker";
// Mark the current vehicle as Sybil
Detected[make_pair(receivedMac, vehicleId)] = "sybil";
return true;
}
}
}
return false;
}
// Callback to handle Wave Short Messages (WSMs) received by RSU
bool ReceivePacketForRSU(Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol, const Address& from) {
Ptr<Node> receivingNode = device->GetNode();
uint32_t receiverNodeId = receivingNode->GetId(); // Get the node ID
ostringstream receiverNodeNameStream;
receiverNodeNameStream << "Node-" << receiverNodeId;
string receiverNodeName = receiverNodeNameStream.str();
if (Mac48Address::IsMatchingType(from)) {
Mac48Address macAddr = Mac48Address::ConvertFrom(from); // Handle MAC address
ostringstream oss;
oss << macAddr;
string receivedMac = oss.str(); // Full MAC with prefix
string macWithoutPrefix = receivedMac.substr(6, 17); // Remove prefix for comparison
uint8_t buffer[100];
packet->CopyData(buffer, packet->GetSize());
string packetContent = string(buffer, buffer + packet->GetSize());
// Extract vehicle ID, signature, and message from the packet content
string vehicleId = packetContent.substr(0, 2); // First 2 chars are Vehicle ID
size_t space1 = packetContent.find(" ", 3); // Find the first space after the vehicle ID
string signature = packetContent.substr(3, space1 - 3); // Extract the signature
string message = packetContent.substr(space1 + 1); // The rest is the message
// Log detailed packet content
cout << "[RECEIVE] RSU " << receiverNodeName << " received packet details:"
<< "\n Vehicle ID: " << vehicleId
<< "\n Signature: " << signature
<< "\n Message: " << message
<< "\n MAC Address: " << receivedMac << endl;
// Check for linked signature in store
GroupSignature newSignature = {signature, vehicleId, macWithoutPrefix};
// Sybil detection
bool sybilDetected = DetectSybilAttack(vehicleId, macWithoutPrefix, newSignature);
if (!sybilDetected) {
// First-time communication or repeated communication with the same MAC
bool found = false;
for (auto& entry : signatureStore) {
if (AreSignaturesLinked(entry.second, newSignature)) {
// Found a linked signature: Check MAC
found = true;
if (entry.first.second == macWithoutPrefix) {
// Same vehicle communicating again, update the entry
signatureStore.erase(entry.first); // Remove old entry
signatureStore[make_pair(vehicleId, macWithoutPrefix)] = newSignature; // Add updated entry
cout << "[UPDATE] RSU updated signature for Vehicle ID: " << vehicleId << endl;
}
}
}
if (!found) {
// No linked signature found, add a new entry for first-time communication
signatureStore[make_pair(vehicleId, macWithoutPrefix)] = newSignature;
cout << "[NEW] RSU created new entry for Vehicle ID: " << vehicleId << endl;
}
} else {
// Sybil detected, do not add to signature store
cout << "[IGNORE] Sybil packet detected, not added to signature store." << endl;
}
} else {
cout << "[RECEIVE] RSU received a packet with non-MAC address type" << endl;
}
return true;
}
// Callback function for promiscuous packet reception
bool PromiscReceivePacket(Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol, const Address& sender, const Address& receiver, NetDevice::PacketType packetType) {
cout << "[PROMISC-RECEIVE] RSU received a packet in promiscuous mode." << endl;
cout << "Sender MAC: " << Mac48Address::ConvertFrom(sender) << endl;
cout << "Receiver MAC: " << Mac48Address::ConvertFrom(receiver) << endl;
cout << "Packet Type: " << packetType << endl;
// You can now pass the packet to your existing ReceivePacketForRSU logic if desired.
return ReceivePacketForRSU(device, packet, protocol, sender);
}
// Simple packet receive handler for vehicles (no Sybil detection for vehicles)
bool ReceivePacketForVehicle(Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol, const Address& from) {
Ptr<Node> receivingNode = device->GetNode();
uint32_t receiverNodeId = receivingNode->GetId(); // Get the node ID
ostringstream receiverNodeNameStream;
receiverNodeNameStream << "Node-" << receiverNodeId;
string receiverNodeName = receiverNodeNameStream.str();
if (Mac48Address::IsMatchingType(from)) {
uint8_t buffer[100];
packet->CopyData(buffer, packet->GetSize());
string packetContent = string(buffer, buffer + packet->GetSize());
string vehicleId = packetContent.substr(0, 2); // First 2 chars are Vehicle ID
string message = packetContent.substr(3); // Rest is the message content
if (message == "beacon") {
cout << "[RECEIVE] " << receiverNodeName << " processed beacon packet from node " << vehicleId << endl;
} else {
cout << "[RECEIVE] " << receiverNodeName << " processed normal packet from node " << vehicleId << endl;
}
} else {
cout << "Received packet with non-MAC address type" << endl;
}
return true;
}
// Function to send a Wave Short Message (WSM) with the signature included in the message
void SendWavePacket(Ptr<WaveNetDevice> device, string vehicleId, string message, string privateKey, Ipv4Address destination) {
// Generate signature based on private key and message
string signature = GenerateGroupSignature(privateKey, message);
ostringstream oss;
oss << device->GetAddress(); // Get MAC address of the device
string actualMac = oss.str().substr(6, 17); // Get MAC address without prefix
string formattedMacWithPrefix = oss.str(); // MAC address with prefix
if (vehicleId == "S1") {
// Spoof the MAC address for vehicle ID 'S1'
string spoofedMac = "00:00:00:00:00:12"; // Example spoofed MAC address
Mac48Address spoofedMacAddr(spoofedMac.c_str());
formattedMacWithPrefix.replace(0, 23, spoofedMac); // Maintain prefix, replace MAC part
cout << "[SPOOFED] Vehicle ID: " << vehicleId << " using spoofed MAC: " << formattedMacWithPrefix << endl;
// Include vehicle ID, message, and signature in the packet content
string packetContent = vehicleId + " " + signature + " " + message;
Ptr<Packet> packet = Create<Packet>(reinterpret_cast<const uint8_t*>(packetContent.c_str()), packetContent.size());
// Use SendFrom with the spoofed MAC address
if (device->SendFrom(packet, spoofedMacAddr, Mac48Address::GetBroadcast(), 0x88DC)) {
cout << "[SEND] Node " << vehicleId << " transmitted " << message << " with signature " << signature << " and spoofed MAC " << spoofedMac << endl;
} else {
cout << "[ERROR] Failed to send packet from Node " << vehicleId << " with spoofed MAC " << spoofedMac << endl;
}
} else {
// Normal case: Use actual MAC (without prefix)
string packetContent = vehicleId + " " + signature + " " + message;
Ptr<Packet> packet = Create<Packet>(reinterpret_cast<const uint8_t*>(packetContent.c_str()), packetContent.size());
// Send normally with the actual MAC using SendX
TxInfo txInfo;
txInfo.channelNumber = CCH; // Control Channel for WAVE
txInfo.priority = 7;
cout<<device->SendX(packet, Mac48Address::GetBroadcast(), 0x88DC, txInfo);
cout << "[SEND] Node " << vehicleId << " transmitted " << message << " with signature " << signature << " and actual MAC " << formattedMacWithPrefix << endl;
}
}
// Function to simulate the TRSU assigning private keys
string RegisterVehicleAtTRSU(string vehicleId) {
cout << "[REGISTER] TRSU T1 assigned private key to node " << vehicleId << endl;
return "PrivateKey_" + vehicleId; // Simulated private key generation
}
int main(int argc, char* argv[]) {
// LogComponentEnable("WaveNetDevice", LOG_LEVEL_ALL);
// LogComponentEnable("YansWifiPhy", LOG_LEVEL_ALL);
// LogComponentEnable("WifiMac", LOG_LEVEL_ALL);
uint32_t simTime = 20;
uint32_t vehCount = 2; // 2 vehicles: V1 and A1
NodeContainer vehicles;
vehicles.Create(vehCount); // V1 and A1
NodeContainer rsuNodes;
rsuNodes.Create(2); // TRSU (T1) and RSU (R1)
// Install mobility
MobilityHelper mobility;
mobility.SetPositionAllocator("ns3::GridPositionAllocator",
"MinX", DoubleValue(0.0),
"MinY", DoubleValue(0.0),
"DeltaX", DoubleValue(5.0),
"DeltaY", DoubleValue(10.0),
"GridWidth", UintegerValue(2),
"LayoutType", StringValue("RowFirst"));
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
mobility.Install(vehicles);
mobility.Install(rsuNodes);
// Install WAVE devices on RSU and vehicles
YansWifiChannelHelper waveChannel = YansWifiChannelHelper::Default();
YansWavePhyHelper wavePhy = YansWavePhyHelper::Default();
wavePhy.SetChannel(waveChannel.Create());
QosWaveMacHelper waveMac = QosWaveMacHelper::Default();
WaveHelper waveHelper = WaveHelper::Default();
NetDeviceContainer vehicleDevices = waveHelper.Install(wavePhy, waveMac, vehicles);
NetDeviceContainer rsuDevices = waveHelper.Install(wavePhy, waveMac, rsuNodes);
// Assign IP addresses
InternetStackHelper internet;
internet.Install(vehicles);
internet.Install(rsuNodes);
Ipv4AddressHelper ipv4;
ipv4.SetBase("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer vehicleInterfaces = ipv4.Assign(vehicleDevices);
Ipv4InterfaceContainer rsuInterfaces = ipv4.Assign(rsuDevices);
// Set RSU to receive Wave Short Messages (WSM)
Ptr<WaveNetDevice> rsuWaveDevice = DynamicCast<WaveNetDevice>(rsuDevices.Get(1));
rsuWaveDevice->SetReceiveCallback(MakeCallback(&ReceivePacketForRSU));
// Enable promiscuous mode by setting the promiscuous receive callback
rsuWaveDevice->SetPromiscReceiveCallback(MakeCallback(&PromiscReceivePacket)); // Enable promiscuous mode
// Vehicle Registration (Private Key Distribution)
string privateKeyV1 = RegisterVehicleAtTRSU("V1");
string privateKeyA1 = RegisterVehicleAtTRSU("A1");
// Schedule beacon and normal message transmissions with signatures
Ptr<WaveNetDevice> v1WaveDevice = DynamicCast<WaveNetDevice>(vehicleDevices.Get(0));
Ptr<WaveNetDevice> a1WaveDevice = DynamicCast<WaveNetDevice>(vehicleDevices.Get(1));
Simulator::Schedule(Seconds(2.0), &SendWavePacket, v1WaveDevice, "V1", "beacon", privateKeyV1, Ipv4Address("255.255.255.255"));
Simulator::Schedule(Seconds(4.0), &SendWavePacket, a1WaveDevice, "A1", "beacon", privateKeyA1, Ipv4Address("255.255.255.255"));
// Attacker A1 sends packets on behalf of Sybil node S1 with spoofed MAC
Simulator::Schedule(Seconds(6.0), &SendWavePacket, a1WaveDevice, "S1", "beacon", privateKeyA1, Ipv4Address("255.255.255.255"));
Simulator::Schedule(Seconds(8.0), &SendWavePacket, v1WaveDevice, "V1", "normal", privateKeyV1, Ipv4Address("255.255.255.255"));
Simulator::Schedule(Seconds(10.0), &SendWavePacket, a1WaveDevice, "A1", "normal", privateKeyA1, Ipv4Address("255.255.255.255"));
// Attacker A1 sends packets on behalf of Sybil node S1 with spoofed MAC
Simulator::Schedule(Seconds(12.0), &SendWavePacket, a1WaveDevice, "S1", "normal", privateKeyA1, Ipv4Address("255.255.255.255"));
// Run the simulation
Simulator::Stop(Seconds(simTime));
Simulator::Run();
Simulator::Destroy();
return 0;
}