|
| 1 | +/* |
| 2 | + * Copyright (c) |
| 3 | + * |
| 4 | + * This program is free software; you can redistribute it and/or modify |
| 5 | + * it under the terms of the GNU General Public License version 2 as |
| 6 | + * published by the Free Software Foundation; |
| 7 | + * |
| 8 | + * This program is distributed in the hope that it will be useful, |
| 9 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | + * GNU General Public License for more details. |
| 12 | + * |
| 13 | + * You should have received a copy of the GNU General Public License |
| 14 | + * along with this program; if not, write to the Free Software |
| 15 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 16 | + * |
| 17 | + * Author: Jiasong Bai |
| 18 | + * Modified: Mingyu Ma<[email protected]> |
| 19 | + */ |
| 20 | + |
| 21 | +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_OFF |
| 22 | + |
| 23 | +#ifdef Mutex |
| 24 | +#undef Mutex |
| 25 | +#endif |
| 26 | + |
| 27 | +#ifdef registry_t |
| 28 | +#undef registry_t |
| 29 | +#endif |
| 30 | + |
| 31 | +#include <bm/spdlog/spdlog.h> |
| 32 | +#undef LOG_INFO |
| 33 | +#undef LOG_ERROR |
| 34 | +#undef LOG_DEBUG |
| 35 | + |
| 36 | +#include "ns3/ethernet-header.h" |
| 37 | +#include "ns3/log.h" |
| 38 | +#include "ns3/p4-core-pipeline.h" |
| 39 | +#include "ns3/register_access.h" |
| 40 | +#include "ns3/simulator.h" |
| 41 | +#include "ns3/socket.h" |
| 42 | + |
| 43 | +#include <bm/bm_runtime/bm_runtime.h> |
| 44 | +#include <bm/bm_sim/options_parse.h> |
| 45 | +#include <bm/bm_sim/parser.h> |
| 46 | +#include <bm/bm_sim/phv.h> |
| 47 | + |
| 48 | +NS_LOG_COMPONENT_DEFINE("P4CorePipeline"); |
| 49 | + |
| 50 | +namespace ns3 |
| 51 | +{ |
| 52 | + |
| 53 | +namespace |
| 54 | +{ |
| 55 | + |
| 56 | +struct hash_ex_v1model_pipeline |
| 57 | +{ |
| 58 | + uint32_t operator()(const char* buf, size_t s) const |
| 59 | + { |
| 60 | + const uint32_t p = 16777619; |
| 61 | + uint32_t hash = 2166136261; |
| 62 | + |
| 63 | + for (size_t i = 0; i < s; i++) |
| 64 | + hash = (hash ^ buf[i]) * p; |
| 65 | + |
| 66 | + hash += hash << 13; |
| 67 | + hash ^= hash >> 7; |
| 68 | + hash += hash << 3; |
| 69 | + hash ^= hash >> 17; |
| 70 | + hash += hash << 5; |
| 71 | + return static_cast<uint32_t>(hash); |
| 72 | + } |
| 73 | +}; |
| 74 | + |
| 75 | +struct bmv2_hash_v1model_pipeline |
| 76 | +{ |
| 77 | + uint64_t operator()(const char* buf, size_t s) const |
| 78 | + { |
| 79 | + return bm::hash::xxh64(buf, s); |
| 80 | + } |
| 81 | +}; |
| 82 | + |
| 83 | +} // namespace |
| 84 | + |
| 85 | +// if REGISTER_HASH calls placed in the anonymous namespace, some compiler can |
| 86 | +// give an unused variable warning |
| 87 | +REGISTER_HASH(hash_ex_v1model_pipeline); |
| 88 | +REGISTER_HASH(bmv2_hash_v1model_pipeline); |
| 89 | + |
| 90 | +P4CorePipeline::P4CorePipeline(P4SwitchNetDevice* netDevice, |
| 91 | + bool enableSwap, |
| 92 | + bool enableTracing, |
| 93 | + uint32_t drop_port) |
| 94 | + : bm::Switch(enableSwap), |
| 95 | + m_enableTracing(enableTracing), |
| 96 | + m_enableSwap(enableSwap), |
| 97 | + m_dropPort(drop_port) |
| 98 | +{ |
| 99 | + m_packetId = 0; |
| 100 | + |
| 101 | + add_required_field("standard_metadata", "ingress_port"); |
| 102 | + add_required_field("standard_metadata", "packet_length"); |
| 103 | + add_required_field("standard_metadata", "instance_type"); |
| 104 | + add_required_field("standard_metadata", "egress_spec"); |
| 105 | + add_required_field("standard_metadata", "egress_port"); |
| 106 | + |
| 107 | + force_arith_header("standard_metadata"); |
| 108 | + // force_arith_header("queueing_metadata"); |
| 109 | + force_arith_header("intrinsic_metadata"); |
| 110 | + |
| 111 | + static int switch_id = 1; |
| 112 | + m_p4SwitchId = switch_id++; |
| 113 | + NS_LOG_INFO("Init P4 Switch with ID: " << m_p4SwitchId); |
| 114 | + |
| 115 | + m_switchNetDevice = netDevice; |
| 116 | +} |
| 117 | + |
| 118 | +P4CorePipeline::~P4CorePipeline() |
| 119 | +{ |
| 120 | + NS_LOG_FUNCTION(this); |
| 121 | + NS_LOG_INFO("Destroying P4CorePipeline."); |
| 122 | +} |
| 123 | + |
| 124 | +void |
| 125 | +P4CorePipeline::InitSwitchWithP4(std::string jsonPath, std::string flowTablePath) |
| 126 | +{ |
| 127 | + // Log function entry |
| 128 | + NS_LOG_FUNCTION(this); |
| 129 | + NS_LOG_INFO("Initializing P4CorePipeline."); |
| 130 | + |
| 131 | + // Initialize status flag and Thrift port |
| 132 | + int status = 0; |
| 133 | + static int p4_switch_ctrl_plane_thrift_port = 9090; |
| 134 | + m_thriftPort = p4_switch_ctrl_plane_thrift_port; |
| 135 | + |
| 136 | + // Configure OptionsParser |
| 137 | + bm::OptionsParser opt_parser; |
| 138 | + opt_parser.config_file_path = jsonPath; |
| 139 | + opt_parser.debugger_addr = "ipc:///tmp/bmv2-v1model-" + |
| 140 | + std::to_string(p4_switch_ctrl_plane_thrift_port) + "-debug.ipc"; |
| 141 | + opt_parser.notifications_addr = "ipc:///tmp/bmv2-v1model-" + |
| 142 | + std::to_string(p4_switch_ctrl_plane_thrift_port) + |
| 143 | + "-notifications.ipc"; |
| 144 | + opt_parser.file_logger = |
| 145 | + "/tmp/bmv2-v1model-" + std::to_string(p4_switch_ctrl_plane_thrift_port) + "-pipeline.log"; |
| 146 | + opt_parser.thrift_port = p4_switch_ctrl_plane_thrift_port++; |
| 147 | + opt_parser.console_logging = false; |
| 148 | + |
| 149 | + // Initialize the switch |
| 150 | + status = init_from_options_parser(opt_parser); |
| 151 | + if (status != 0) |
| 152 | + { |
| 153 | + NS_LOG_ERROR("Failed to initialize P4CorePipeline."); |
| 154 | + return; // Avoid exiting simulation |
| 155 | + } |
| 156 | + |
| 157 | + // Start the runtime server |
| 158 | + int port = get_runtime_port(); |
| 159 | + bm_runtime::start_server(this, port); |
| 160 | + |
| 161 | + // Populate flow table using CLI command |
| 162 | + std::string cmd = "simple_switch_CLI --thrift-port " + std::to_string(port) + " < " + |
| 163 | + flowTablePath + " > /dev/null 2>&1"; |
| 164 | + |
| 165 | + int result = std::system(cmd.c_str()); |
| 166 | + |
| 167 | + // Wait for the server to be ready |
| 168 | + sleep(1); |
| 169 | + if (result != 0) |
| 170 | + { |
| 171 | + NS_LOG_ERROR("Error executing flow table population command: " << cmd); |
| 172 | + } |
| 173 | + |
| 174 | + NS_LOG_INFO("P4CorePipeline initialization completed successfully."); |
| 175 | +} |
| 176 | + |
| 177 | +int |
| 178 | +P4CorePipeline::InitFromCommandLineOptions(int argc, char* argv[]) |
| 179 | +{ |
| 180 | + bm::OptionsParser parser; |
| 181 | + parser.parse(argc, argv, m_argParser); |
| 182 | + |
| 183 | + // create a dummy transport |
| 184 | + std::shared_ptr<bm::TransportIface> transport = |
| 185 | + std::shared_ptr<bm::TransportIface>(bm::TransportIface::make_dummy()); |
| 186 | + |
| 187 | + int status = 0; |
| 188 | + if (parser.no_p4) |
| 189 | + // with out p4-json, acctually the switch will wait for the |
| 190 | + // configuration(p4-json) before work |
| 191 | + status = init_objects_empty(parser.device_id, transport); |
| 192 | + else |
| 193 | + // load p4 configuration files xxxx.json to switch |
| 194 | + status = init_objects(parser.config_file_path, parser.device_id, transport); |
| 195 | + return status; |
| 196 | +} |
| 197 | + |
| 198 | +void |
| 199 | +P4CorePipeline::RunCli(const std::string& commandsFile) |
| 200 | +{ |
| 201 | + NS_LOG_FUNCTION(this << " Switch ID: " << m_p4SwitchId << " Running CLI commands from " |
| 202 | + << commandsFile); |
| 203 | + |
| 204 | + int port = get_runtime_port(); |
| 205 | + bm_runtime::start_server(this, port); |
| 206 | + // start_and_return (); |
| 207 | + NS_LOG_DEBUG("Switch ID: " << m_p4SwitchId << " Waiting for the runtime server to start"); |
| 208 | + std::this_thread::sleep_for(std::chrono::seconds(3)); |
| 209 | + |
| 210 | + // Run the CLI commands to populate table entries |
| 211 | + std::string cmd = "run_bmv2_CLI --thrift_port " + std::to_string(port) + " " + commandsFile; |
| 212 | + int res = std::system(cmd.c_str()); |
| 213 | + (void)res; |
| 214 | +} |
| 215 | + |
| 216 | +int |
| 217 | +P4CorePipeline::receive_(uint32_t port_num, const char* buffer, int len) |
| 218 | +{ |
| 219 | + NS_LOG_FUNCTION(this << " Switch ID: " << m_p4SwitchId << " Port: " << port_num |
| 220 | + << " Len: " << len); |
| 221 | + return 0; |
| 222 | +} |
| 223 | + |
| 224 | +void |
| 225 | +P4CorePipeline::start_and_return_() |
| 226 | +{ |
| 227 | + NS_LOG_FUNCTION("Switch ID: " << m_p4SwitchId << " start"); |
| 228 | +} |
| 229 | + |
| 230 | +void |
| 231 | +P4CorePipeline::swap_notify_() |
| 232 | +{ |
| 233 | + NS_LOG_FUNCTION("p4_switch has been notified of a config swap"); |
| 234 | +} |
| 235 | + |
| 236 | +void |
| 237 | +P4CorePipeline::reset_target_state_() |
| 238 | +{ |
| 239 | + NS_LOG_DEBUG("Resetting target-specific state, not supported."); |
| 240 | +} |
| 241 | + |
| 242 | +int |
| 243 | +P4CorePipeline::P4ProcessingPipeline(Ptr<Packet> packetIn, |
| 244 | + int inPort, |
| 245 | + uint16_t protocol, |
| 246 | + const Address& destination) |
| 247 | +{ |
| 248 | + NS_LOG_FUNCTION(this); |
| 249 | + |
| 250 | + // === Convert ns-3 packet to bm packet |
| 251 | + std::unique_ptr<bm::Packet> bm_packet = ConvertToBmPacket(packetIn, inPort); |
| 252 | + |
| 253 | + bm::PHV* phv = bm_packet->get_phv(); |
| 254 | + uint32_t len = bm_packet.get()->get_data_size(); |
| 255 | + bm_packet.get()->set_ingress_port(inPort); |
| 256 | + |
| 257 | + phv->reset_metadata(); |
| 258 | + phv->get_field("standard_metadata.ingress_port").set(inPort); |
| 259 | + bm_packet->set_register(RegisterAccess::PACKET_LENGTH_REG_IDX, len); |
| 260 | + phv->get_field("standard_metadata.packet_length").set(len); |
| 261 | + bm::Field& f_instance_type = phv->get_field("standard_metadata.instance_type"); |
| 262 | + f_instance_type.set(PKT_INSTANCE_TYPE_NORMAL); |
| 263 | + |
| 264 | + // === Parser and MAU processing |
| 265 | + bm::Parser* parser = this->get_parser("parser"); |
| 266 | + bm::Pipeline* ingress_mau = this->get_pipeline("ingress"); |
| 267 | + parser->parse(bm_packet.get()); |
| 268 | + ingress_mau->apply(bm_packet.get()); |
| 269 | + |
| 270 | + bm_packet->reset_exit(); |
| 271 | + bm::Field& f_egress_spec = phv->get_field("standard_metadata.egress_spec"); |
| 272 | + uint32_t egress_spec = f_egress_spec.get_uint(); |
| 273 | + |
| 274 | + // LEARNING |
| 275 | + int learn_id = RegisterAccess::get_lf_field_list(bm_packet.get()); |
| 276 | + if (learn_id > 0) |
| 277 | + { |
| 278 | + get_learn_engine()->learn(learn_id, *bm_packet.get()); |
| 279 | + } |
| 280 | + |
| 281 | + // === Egress |
| 282 | + bm::Pipeline* egress_mau = this->get_pipeline("egress"); |
| 283 | + bm::Deparser* deparser = this->get_deparser("deparser"); |
| 284 | + phv->get_field("standard_metadata.egress_port").set(egress_spec); |
| 285 | + f_egress_spec = phv->get_field("standard_metadata.egress_spec"); |
| 286 | + f_egress_spec.set(0); |
| 287 | + |
| 288 | + phv->get_field("standard_metadata.packet_length") |
| 289 | + .set(bm_packet->get_register(RegisterAccess::PACKET_LENGTH_REG_IDX)); |
| 290 | + |
| 291 | + egress_mau->apply(bm_packet.get()); |
| 292 | + |
| 293 | + // === Deparser |
| 294 | + deparser->deparse(bm_packet.get()); |
| 295 | + |
| 296 | + // === Send the packet to the destination |
| 297 | + Ptr<Packet> ns_packet = ConvertToNs3Packet(std::move(bm_packet)); |
| 298 | + m_switchNetDevice->SendNs3Packet(ns_packet, egress_spec, protocol, destination); |
| 299 | + return 0; |
| 300 | +} |
| 301 | + |
| 302 | +Ptr<Packet> |
| 303 | +P4CorePipeline::ConvertToNs3Packet(std::unique_ptr<bm::Packet>&& bm_packet) |
| 304 | +{ |
| 305 | + // Create a new ns3::Packet using the data buffer |
| 306 | + char* bm_buf = bm_packet.get()->data(); |
| 307 | + size_t len = bm_packet.get()->get_data_size(); |
| 308 | + Ptr<Packet> ns_packet = Create<Packet>((uint8_t*)(bm_buf), len); |
| 309 | + |
| 310 | + return ns_packet; |
| 311 | +} |
| 312 | + |
| 313 | +std::unique_ptr<bm::Packet> |
| 314 | +P4CorePipeline::ConvertToBmPacket(Ptr<Packet> nsPacket, int inPort) |
| 315 | +{ |
| 316 | + int len = nsPacket->GetSize(); |
| 317 | + uint8_t* pkt_buffer = new uint8_t[len]; |
| 318 | + nsPacket->CopyData(pkt_buffer, len); |
| 319 | + bm::PacketBuffer buffer(len + 512, (char*)pkt_buffer, len); |
| 320 | + std::unique_ptr<bm::Packet> bm_packet( |
| 321 | + new_packet_ptr(inPort, m_packetId++, len, std::move(buffer))); |
| 322 | + delete[] pkt_buffer; |
| 323 | + |
| 324 | + return bm_packet; |
| 325 | +} |
| 326 | + |
| 327 | +} // namespace ns3 |
0 commit comments