diff --git a/README.md b/README.md index 843da31..6f35202 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,113 @@ -# ZodiacFX -Firmware for the Northbound Networks Zodiac FX OpenFlow Switch +# Zodiac FX Firmware + +This is the firmware for the [Northbound Networks](https://northboundnetworks.com/) Zodiac FX OpenFlow Switch. + +## Background + +The Zodiac FX began as a [Kickstarter campaign](https://www.kickstarter.com/projects/northboundnetworks/zodiac-fx-the-worlds-smallest-openflow-sdn-switch) in 2015, with the goal of providing an affordable Software Defined Networking (SDN) platform for developers, researchers, and networking hobbyists. To learn more about SDN, visit the [Northbound Networks Blog](https://northboundnetworks.com/blogs/sdn). + +The Zodiac FX SDN switch uses the [OpenFlow protocol](https://www.opennetworking.org/sdn-resources/openflow), an open standard for communication in an SDN architecture. OpenFlow allows communication between the SDN controller (where applications can be run) and OpenFlow switches in the network. These switches use application-generated "Flows" to determine how network data is processed. + +This repository contains the entire open-source firmware for the Zodiac FX including OpenFlow libraries. The firmware is constantly being updated with support for features of the OpenFlow specification, and to improve compatability with the various SDN controllers. OpenFlow version 1.0 and version 1.3 are currently supported. + +## Flashing/Updating the Firmware + +The latest firmware is available in the [Northbound Networks Forums](http://forums.northboundnetworks.com/index.php?topic=52.0). + +Starting from version 0.80, Zodiac FX supports firmware updates via the CLI and web interface. In order to make this possible, major changes were made to the firmware flashing process. Follow the update process below, based on your current firmware version. + +#### For firmware versions BEFORE version 0.80 + +To update to version 0.80 or later, a **full upgrade firmware** needs to be flashed. + +Download the latest **full upgrade firmware** from the [Northbound Networks Forums](http://forums.northboundnetworks.com/index.php?topic=52.0) - 'Full Upgrade Firmware (v0.XX) - ZodiacFX_vXX_full_install.bin' + +Follow the firmware update process detailed in **Section 2. Updating Firmware** in the [Zodiac FX User Guide](http://forums.northboundnetworks.com/downloads/zodiac_fx/guides/ZodiacFX_UserGuide_0317.pdf). + +#### For firmware versions AFTER version 0.80 + +The update process has been simplified for the newer releases. + +Download the latest **update firmware** from the [Northbound Networks Forums](http://forums.northboundnetworks.com/index.php?topic=52.0) - 'Update Firmware (v0.XX) - ZodiacFX_vXX_update.bin' + +* **To update via the CLI**: + * In the root context, type the 'update' command + * When prompted, begin the firmware update via the XMODEM protocol + * Note: not all serial terminal applications support XMODEM + * If the firmware update is successful, Zodiac FX will automatically restart to complete the update + +* **To update via the web interface**: + * Go to the 'Update f/w' page in the Zodiac FX web interface + * Note: the web interface is available by going to the Zodiac FX IP address in a web browser on the controller + * This feature is currently fully supported in Google Chrome + * Browse and select the downloaded firmware + * Click 'Upload File' and wait for a confirmation page to appear + * Click 'Restart' in the web interface header to complete the update + +* **[Advanced] To update via cURL**: + * Run the following command: **curl --verbose -0 --form "file=@ZodiacFX_vXX_update.bin"** + * If the firmware upload fails, you may need to use the multipart/related content type like so: **curl -H "Content-Type: multipart/related" --verbose -0 --form "file=@ZodiacFX_vXX_update.bin"** + * Note: a restart is required after the update to load the new firmware. + +## Building the Project + +If you want to modify the firmware for your own project, either download this project or fork this repository. + +[Atmel Studio 7](https://www.atmel.com/Microsite/atmel-studio/) is required to build the project. + +#### Debugging the project + +For full source-level debugging, a [Zodiac FX Hardware Debugger](https://northboundnetworks.com/products/zodiac-fx-hardware-debugger) is required. + +* Ensure that the hardware debugger appears in Project -> ZodiacFX Properties -> Tool -> Selected debugger/programmer + * Zodiac FX uses the JTAG interface +* Select the 'Debug' Solution Configuration in the Atmel Studio Standard toolbar + * The 'Release' configuration has been modified to build firmware updates, and will not run directly in Atmel Studio +* Select the 'Start Debugging and Break' option in the Debug menu to begin stepping through the source + +The firmware will continue to run by cycling power to the Zodiac FX (after removing the hardware debugger). However, firmware updating will not function until a **full upgrade firmware** is flashed. The modified firmware can be written to the Zodiac FX by following the steps outlined below: **running the code without a hardware debugger**. + +#### Running the code without a hardware debugger + +Modified firmware can be tested without the Zodiac FX Hardware Debugger, however source-level debugging is not possible. + +* Build the 'Release' configuration of the ZodiacFX solution +* Navigate to the compiled binary + * \ZodiacFX\Release\ZodiacFX.bin +* Follow the steps in Signing Binaries to allow the Zodiac FX to update to the modified firmware + +## Signing Binaries + +The Zodiac FX uses a simple additive checksum to verify the integrity of the uploaded firmware. + +To sign your own modified firmware, follow the steps below: +* Build a 'Release' binary of the modified firmware +* Update the Zodiac FX with the modified firmware + * Follow the instructions outlined in Flashing/Updating the Firmware - For firmware versions AFTER version 0.80 +* The firmware will fail the verification check, but will still be stored inside the Zodiac FX flash memory +* In the root context of the CLI, type in the hidden command 'get crc' +* Open the ZodiacFX.bin file in a hex editor, and append the 8 bytes to the end of the firmware file + * For example, if 'get crc' provides [A05A1201 00000000], append 'A0 5A 12 01 00 00 00 00' to the end of the firmware file +* Update the Zodiac FX again with the (now signed) modified firmware +* The firmware update should be successful, and the Zodiac FX will run the new firmware after restarting + +## Reporting Bugs and Issues + +Any bugs and issues can be brought up in the [Northbound Networks Forums](http://forums.northboundnetworks.com/index.php?board=3.0). + +Issues can also be [raised](https://github.com/NorthboundNetworks/ZodiacFX/issues) in this repository. + +## Release Notes + +**Version 0.80** +* Firmware upload via CLI and web interface added +* Metering added to OpenFlow 1.3 + +## Authors + +* **Paul Zanna** - Creator +* **Kristopher Chen** - Firmware Developer + +## License + +[GPL 3.0](LICENSE) diff --git a/ZodiacFX/ZodiacFX.cproj b/ZodiacFX/ZodiacFX.cproj index 7af9923..e3e4036 100644 --- a/ZodiacFX/ZodiacFX.cproj +++ b/ZodiacFX/ZodiacFX.cproj @@ -274,11 +274,11 @@ JTAG com.atmel.avrdbg.tool.atmelice - J41800009874 + J41800058832 Atmel-ICE JTAG - J41800009874 + J41800058832 0xA3CC0CE0 7500000 @@ -368,7 +368,7 @@ True - -Wl,--defsym,__stack_size__=0x1400 -Wl,--entry=Reset_Handler -Wl,--cref -mthumb -T../src/ASF/sam/utils/linker_scripts/sam4e/sam4e8/gcc/flash.ld + -Wl,--defsym,__stack_size__=0x1400 -Wl,--entry=Reset_Handler -Wl,-section-start=.text=0x00420000 -Wl,--cref -mthumb -T../src/ASF/sam/utils/linker_scripts/sam4e/sam4e8/gcc/flash.ld ../src/ASF/sam/drivers/efc diff --git a/ZodiacFX/src/command.c b/ZodiacFX/src/command.c index 38c4065..864f33d 100644 --- a/ZodiacFX/src/command.c +++ b/ZodiacFX/src/command.c @@ -48,6 +48,7 @@ // Global variables extern struct zodiac_config Zodiac_Config; +extern struct verification_data verify; extern bool debug_output; extern int charcount, charcount_last; @@ -62,6 +63,8 @@ extern int iLastFlow; extern struct ofp10_port_stats phys10_port_stats[4]; extern struct ofp13_port_stats phys13_port_stats[4]; extern struct table_counter table_counters[MAX_TABLES]; +extern struct meter_entry13 *meter_entry[MAX_METER_13]; +extern struct meter_band_stats_array band_stats_array[MAX_METER_13]; extern bool masterselect; extern bool stackenabled = false; extern bool trace = false; @@ -71,6 +74,7 @@ extern int totaltime; extern int32_t ul_temp; extern int OF_Version; extern uint32_t uid_buf[4]; +extern bool restart_required_outer; // Local Variables bool showintro = true; @@ -135,6 +139,15 @@ void task_command(char *str, char *str_last) char *param3; char *pch; + if(restart_required_outer == true) + { + printf("Restarting the Zodiac FX, please reopen your terminal application.\r\n"); + for(int x = 0;x<100000;x++); // Let the above message get sent to the terminal before detaching + udc_detach(); // Detach the USB device before restart + rstc_start_software_reset(RSTC); // Software reset + while (1); + } + while(udi_cdc_is_rx_ready()){ ch = udi_cdc_getc(); @@ -440,6 +453,15 @@ void command_root(char *command, char *param1, char *param2, char *param3) while (1); } + // Get CRC + if (strcmp(command, "get")==0 && strcmp(param1, "crc")==0) + { + verification_check(); + printf("Calculated verification: %08x\r\n", verify.calculated); + printf("Append [%08x 00000000] to the binary\r\n", ntohl(verify.calculated)); + return; + } + // Unknown Command printf("Unknown command\r\n"); return; @@ -1021,7 +1043,6 @@ void command_openflow(char *command, char *param1, char *param2, char *param3) int match_size; int inst_size; int act_size; - struct ofp13_instruction *inst_ptr; struct ofp13_instruction_actions *inst_actions; struct oxm_header13 oxm_header; uint8_t oxm_value8; @@ -1146,22 +1167,38 @@ void command_openflow(char *command, char *param1, char *param2, char *param3) int min = t/60; int sec = t%60; printf(" Last Match: %02d:%02d:%02d\r\n", hr, min, sec); + // Print instruction list if (ofp13_oxm_inst[i] != NULL) { + // Get a list of all instructions for this flow + void *insts[8] = {0}; + inst_size = 0; + while(inst_size < ofp13_oxm_inst_size[i]){ + struct ofp13_instruction *inst_ptr = (struct ofp13_instruction *)(ofp13_oxm_inst[i] + inst_size); + insts[ntohs(inst_ptr->type)] = inst_ptr; + inst_size += ntohs(inst_ptr->len); + } + printf("\r Instructions:\r\n"); - inst_ptr = (struct ofp13_instruction *) ofp13_oxm_inst[i]; - inst_size = ntohs(inst_ptr->len); - if(ntohs(inst_ptr->type) == OFPIT13_APPLY_ACTIONS) + + // Check for optional metering instruction + if(insts[OFPIT13_METER] != NULL) + { + struct ofp13_instruction_meter *inst_meter = insts[OFPIT13_METER]; + printf(" Meter: %d\r\n", ntohl(inst_meter->meter_id)); + } + + if(insts[OFPIT13_APPLY_ACTIONS] != NULL) { printf(" Apply Actions:\r\n"); struct ofp13_action_header *act_hdr; act_size = 0; - if (inst_size == sizeof(struct ofp13_instruction_actions)) printf(" DROP \r\n"); // No actions - while (act_size < (inst_size - sizeof(struct ofp13_instruction_actions))) + inst_actions = insts[OFPIT13_APPLY_ACTIONS]; + if (ntohs(inst_actions->len) == sizeof(struct ofp13_instruction_actions)) printf(" DROP \r\n"); // No actions + while (act_size < (ntohs(inst_actions->len) - sizeof(struct ofp13_instruction_actions))) { - inst_actions = ofp13_oxm_inst[i] + act_size; - act_hdr = &inst_actions->actions; + act_hdr = (struct ofp13_action_header*)((uintptr_t)inst_actions->actions + act_size); if (htons(act_hdr->type) == OFPAT13_OUTPUT) { struct ofp13_action_output *act_output = act_hdr; @@ -1304,26 +1341,11 @@ void command_openflow(char *command, char *param1, char *param2, char *param3) } } // Print goto table instruction - if(ntohs(inst_ptr->type) == OFPIT13_GOTO_TABLE) + if(insts[OFPIT13_GOTO_TABLE] != NULL) { struct ofp13_instruction_goto_table *inst_goto_ptr; - inst_goto_ptr = (struct ofp13_instruction_goto_table *) inst_ptr; + inst_goto_ptr = (struct ofp13_instruction_goto_table *) insts[OFPIT13_GOTO_TABLE]; printf(" Goto Table: %d\r\n", inst_goto_ptr->table_id); - continue; - } - // Is there more then one instruction? - if (ofp13_oxm_inst_size[i] > inst_size) - { - uint8_t *nxt_inst; - nxt_inst = ofp13_oxm_inst[i] + inst_size; - inst_ptr = (struct ofp13_instruction *) nxt_inst; - inst_size = ntohs(inst_ptr->len); - if(ntohs(inst_ptr->type) == OFPIT13_GOTO_TABLE) - { - struct ofp13_instruction_goto_table *inst_goto_ptr; - inst_goto_ptr = (struct ofp13_instruction_goto_table *) inst_ptr; - printf(" Goto Table: %d\r\n", inst_goto_ptr->table_id); - } } } else { // No instructions @@ -1463,6 +1485,90 @@ void command_openflow(char *command, char *param1, char *param2, char *param3) return; } + // Show meter table entries + if (strcmp(command, "show") == 0 && strcmp(param1, "meters") == 0) + { + int meter_out_counter = 1; + + // Check that table is populated + if(meter_entry[0] != NULL) + { + int meter_index = 0; + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + printf("\r\n-------------------------------------------------------------------------\r\n"); + printf("\r\nMeter %d\r\n", meter_out_counter); + meter_out_counter++; + printf(" Meter ID: %d\r\n", meter_entry[meter_index]->meter_id); + printf(" Counters:\r\n"); + meter_entry[meter_index]->flow_count = get_bound_flows(meter_entry[meter_index]->meter_id); + printf("\tBound Flows:\t%d\tDuration:\t%d sec\r\n", meter_entry[meter_index]->flow_count, (sys_get_ms()-meter_entry[meter_index]->time_added)/1000); + printf("\tByte Count:\t%"PRIu64"\tPacket Count:\t%"PRIu64"\r\n", meter_entry[meter_index]->byte_in_count, meter_entry[meter_index]->packet_in_count); + printf("\tConfiguration:\t"); + if(((meter_entry[meter_index]->flags) & OFPMF13_KBPS) == OFPMF13_KBPS) + { + printf("KBPS; "); + } + if(((meter_entry[meter_index]->flags) & OFPMF13_PKTPS) == OFPMF13_PKTPS) + { + printf("PKTPS; "); + } + if(((meter_entry[meter_index]->flags) & OFPMF13_BURST) == OFPMF13_BURST) + { + printf("BURST; "); + } + if(((meter_entry[meter_index]->flags) & OFPMF13_STATS) == OFPMF13_STATS) + { + printf("STATS; "); + } + if(meter_entry[meter_index]->flags == 0) + { + printf(" NONE;"); + } + + printf("\r\n\tNumber of bands:\t%d\r\n", meter_entry[meter_index]->band_count); + int bands_processed = 0; + struct ofp13_meter_band_drop * ptr_band; + ptr_band = &(meter_entry[meter_index]->bands); + while(bands_processed < meter_entry[meter_index]->band_count) + { + printf("\t\tBand %d:\r\n", bands_processed+1); + printf("\t\t Type:\t\t"); + if(ptr_band->type == OFPMBT13_DROP) + { + printf("DROP\r\n"); + } + else if(ptr_band->type == OFPMBT13_DSCP_REMARK) + { + printf("DSCP REMARK (unsupported)\r\n"); + } + else + { + printf("unsupported type\r\n"); + } + printf("\t\t Rate:\t\t%d\t\r\n", ptr_band->rate); + printf("\t\t Burst Size:\t%d\t\r\n", ptr_band->burst_size); + + // Find band index + int band_index = ((uint8_t*)ptr_band - (uint8_t*)&(meter_entry[meter_index]->bands)) / sizeof(struct ofp13_meter_band_drop); + + // Display counters + printf("\t\t Byte count:\t%"PRIu64"\t\r\n", band_stats_array[meter_index].band_stats[band_index].byte_band_count); + printf("\t\t Packet count:\t%"PRIu64"\t\r\n", band_stats_array[meter_index].band_stats[band_index].packet_band_count); + + ptr_band++; // Move to next band + bands_processed++; + } + meter_index++; + } + printf("\r\n-------------------------------------------------------------------------\r\n\r\n"); + } + else + { + printf("No meters configured.\r\n"); + } + return; + } // Unknown Command printf("Unknown command\r\n"); return; @@ -1525,36 +1631,7 @@ void command_debug(char *command, char *param1, char *param2, char *param3) trace = true; printf("Starting trace...\r\n"); return; - } - - if (strcmp(command, "check_flash")==0) - { - // Display contents of firmware update region (ending @ first 0xFFFFFFFF) - unsigned long* pmem = (unsigned long*)0x00450000; - while(pmem <= 0x00480000) - { - if(*pmem == 0xFFFFFFFF) - { - return; - } - printf("Addr: %p Val: 0x%l08x\n\r", (void *)pmem, *pmem); - pmem++; - } - return; - } - - if (strcmp(command, "check_flash_all")==0) - { - // Display contents of firmware update region - unsigned long* pmem = (unsigned long*)0x00450000; - while(pmem <= 0x00480000) - { - printf("Addr: %p Val: 0x%l08x\n\r", (void *)pmem, *pmem); - pmem++; - } - return; - } - + } // Unknown Command response printf("Unknown command\r\n"); @@ -1624,6 +1701,7 @@ void printhelp(void) printf("OpenFlow:\r\n"); printf(" show status\r\n"); printf(" show flows\r\n"); + printf(" show meters\r\n"); printf(" enable\r\n"); printf(" disable\r\n"); printf(" clear flows\r\n"); diff --git a/ZodiacFX/src/config/conf_membag.h b/ZodiacFX/src/config/conf_membag.h index e0344d5..a3dcf40 100644 --- a/ZodiacFX/src/config/conf_membag.h +++ b/ZodiacFX/src/config/conf_membag.h @@ -57,7 +57,7 @@ MEMBAG(56, 32), MEMBAG(56, 32), MEMBAG(56, 32), MEMBAG(56, 32),\ MEMBAG(72, 32), MEMBAG(72, 32), MEMBAG(72, 32), MEMBAG(72, 32),\ MEMBAG(72, 32), MEMBAG(72, 32), MEMBAG(72, 32), MEMBAG(72, 32),\ - MEMBAG(96, 32), MEMBAG(96, 32), MEMBAG(96, 32), MEMBAG(96, 32), + MEMBAG(96, 32), MEMBAG(96, 32), MEMBAG(96, 32), MEMBAG(96, 32) #define CONF_MEMBAG_POOL_SIZE\ MEMBAG_SIZE(16, 32) + MEMBAG_SIZE(16, 32) + MEMBAG_SIZE(16, 32) + MEMBAG_SIZE(16, 32) +\ @@ -67,6 +67,6 @@ MEMBAG_SIZE(56, 32) + MEMBAG_SIZE(56, 32) + MEMBAG_SIZE(56, 32) + MEMBAG_SIZE(56, 32) +\ MEMBAG_SIZE(72, 32) + MEMBAG_SIZE(72, 32) + MEMBAG_SIZE(72, 32) + MEMBAG_SIZE(72, 32) +\ MEMBAG_SIZE(72, 32) + MEMBAG_SIZE(72, 32) + MEMBAG_SIZE(72, 32) + MEMBAG_SIZE(72, 32) +\ - MEMBAG_SIZE(96, 32) + MEMBAG_SIZE(96, 32) + MEMBAG_SIZE(96, 32) + MEMBAG_SIZE(96, 32) + MEMBAG_SIZE(96, 32) + MEMBAG_SIZE(96, 32) + MEMBAG_SIZE(96, 32) + MEMBAG_SIZE(96, 32) #endif /* CONF_MEMBAG_H */ diff --git a/ZodiacFX/src/config/config_zodiac.h b/ZodiacFX/src/config/config_zodiac.h index 98ee342..6698768 100644 --- a/ZodiacFX/src/config/config_zodiac.h +++ b/ZodiacFX/src/config/config_zodiac.h @@ -31,7 +31,7 @@ #define CONFIG_ZODIAC_H_ -#define VERSION "0.72" // Firmware version number +#define VERSION "0.80" // Firmware version number #define MAX_OFP_VERSION 0x04 @@ -46,4 +46,9 @@ #define HB_TIMEOUT 6 // Number of seconds to wait when there is no response from the controller +#define MAX_OF_STATS 15 // Maximum number of flows to send to controller + +#define MAX_METER_13 8 // Maximum number of meter entries in meter table +#define MAX_METER_BANDS_13 3 // Maximum number of meter bands per meter + #endif /* CONFIG_ZODIAC_H_ */ diff --git a/ZodiacFX/src/config/lwipopts.h b/ZodiacFX/src/config/lwipopts.h index b488894..8c7d60b 100644 --- a/ZodiacFX/src/config/lwipopts.h +++ b/ZodiacFX/src/config/lwipopts.h @@ -101,7 +101,7 @@ * MEM_SIZE: the size of the heap memory. If the application will send * a lot of data that needs to be copied, this should be set high. */ -#define MEM_SIZE 4 * 1024 +#define MEM_SIZE 6 * 1024 /** * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One @@ -126,7 +126,7 @@ * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. * (requires the LWIP_TCP option) */ -#define MEMP_NUM_TCP_SEG 16 +#define MEMP_NUM_TCP_SEG 25 /** * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for @@ -223,7 +223,7 @@ * when opening a connection. For the transmit size, this MSS sets * an upper limit on the MSS advertised by the remote host. */ -#define TCP_MSS 1460 +#define TCP_MSS 536 /** * TCP_WND: The size of a TCP window. This must be at least @@ -235,7 +235,7 @@ * TCP_SND_BUF: TCP sender buffer space (bytes). * To achieve good performance, this should be at least 2 * TCP_MSS. */ -#define TCP_SND_BUF (2 * TCP_MSS) +#define TCP_SND_BUF (6 * TCP_MSS) /** * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least diff --git a/ZodiacFX/src/flash.c b/ZodiacFX/src/flash.c index 0e345c8..25ac197 100644 --- a/ZodiacFX/src/flash.c +++ b/ZodiacFX/src/flash.c @@ -34,9 +34,12 @@ #include "flash.h" #include "config_zodiac.h" #include "openflow/openflow.h" +#include "trace.h" +#include "command.h" // Global variables extern uint8_t shared_buffer[SHARED_BUFFER_LEN]; +struct verification_data verify; // Static variables static uint32_t page_addr; @@ -113,6 +116,7 @@ int firmware_update_init(void) */ int flash_write_page(uint8_t *flash_page) { + TRACE("flash.c: writing to 0x%08x", flash_page_addr); if(flash_page_addr <= IFLASH_ADDR + IFLASH_SIZE - IFLASH_PAGE_SIZE) { ul_rc = flash_write(flash_page_addr, flash_page, @@ -145,13 +149,97 @@ void cli_update(void) { printf("Error: failed to write firmware to memory\r\n"); } - printf("Firmware upload complete - Restarting the Zodiac FX.\r\n"); - for(int x = 0;x<100000;x++); // Let the above message get send to the terminal before detaching - udc_detach(); // Detach the USB device before restart - rstc_start_software_reset(RSTC); // Software reset + if(verification_check() == SUCCESS) + { + printf("Firmware upload complete - Restarting the Zodiac FX.\r\n"); + for(int x = 0;x<100000;x++); // Let the above message get send to the terminal before detaching + udc_detach(); // Detach the USB device before restart + rstc_start_software_reset(RSTC); // Software reset + } + else + { + printf("\r\n"); + printf("Firmware verification check failed\r\n"); + printf("\r\n"); + } + return; } +/* +* Verify firmware data +* +*/ +int verification_check(void) +{ + char* fw_end_pmem = (char*)FLASH_BUFFER_END; // Buffer pointer to store the last address + char* fw_step_pmem = (char*)FLASH_BUFFER; // Buffer pointer to the starting address + uint32_t crc_sum = 0; // Store CRC sum + uint8_t pad_error = 0; // Set when padding is not found + + /* Add all bytes of the uploaded firmware */ + // Decrement the pointer until the previous address has data in it (not 0xFF) + while(*(fw_end_pmem-1) == '\xFF' && fw_end_pmem > FLASH_BUFFER) + { + fw_end_pmem--; + } + + for(int sig=1; sig<=4; sig++) + { + if(*(fw_end_pmem-sig) != NULL) + { + TRACE("signature padding %d not found - last address: %08x", sig, fw_end_pmem); + pad_error = 1; + } + else + { + TRACE("signature padding %d found", sig); + } + } + + // Start summing all bytes + if(pad_error) + { + // Calculate CRC for debug + while(fw_step_pmem < fw_end_pmem) + { + crc_sum += *fw_step_pmem; + fw_step_pmem++; + } + } + else + { + // Exclude CRC & padding from calculation + while(fw_step_pmem < (fw_end_pmem-8)) + { + crc_sum += *fw_step_pmem; + fw_step_pmem++; + } + } + + TRACE("fw_step_pmem %08x; fw_end_pmem %08x;", fw_step_pmem, fw_end_pmem); + + // Update structure entry + TRACE("CRC sum: %04x", crc_sum); + verify.calculated = crc_sum; + + /* Compare with last 4 bytes of firmware */ + // Get last 4 bytes of firmware (4-byte CRC, 4-byte padding) + verify.found = *(uint32_t*)(fw_end_pmem - 8); + + TRACE("CRC found: %04x", verify.found); + + // Compare calculated and found CRC + if(verify.found == verify.calculated) + { + return SUCCESS; + } + else + { + return FAILURE; + } +} + /* * XModem transfer * @@ -159,7 +247,8 @@ void cli_update(void) int xmodem_xfer(void) { char ch; - int timeout_clock = 0; + int timeout_clock = 0; // protocol (NAK) timeout counter + int timeout_upload = 0; // upload timeout counter int buff_ctr = 1; int byte_ctr = 1; int block_ctr = 0; @@ -237,10 +326,15 @@ int xmodem_xfer(void) byte_ctr++; } timeout_clock++; - if (timeout_clock > 1000000) // Timeout, send + if(timeout_upload > 6) + { + return; + } + else if (timeout_clock > 1000000) // Timeout, send { printf("%c", X_NAK); timeout_clock = 0; + timeout_upload++; } } } diff --git a/ZodiacFX/src/flash.h b/ZodiacFX/src/flash.h index 06f1fde..7c25a9e 100644 --- a/ZodiacFX/src/flash.h +++ b/ZodiacFX/src/flash.h @@ -40,6 +40,14 @@ __no_inline RAMFUNC void firmware_update(void); int xmodem_xfer(void); void xmodem_clear_padding(uint8_t *buff); +int verification_check(void); + +struct verification_data +{ + uint32_t calculated; // Last 4 bytes from summed data + uint32_t found; // 4 bytes at the end of uploaded firmware +}; + #define X_EOT 0x04 #define X_ACK 0x06 #define X_NAK 0x15 @@ -48,5 +56,9 @@ void xmodem_clear_padding(uint8_t *buff); #define NEW_FW_BASE (IFLASH_ADDR + (5*IFLASH_NB_OF_PAGES/8)*IFLASH_PAGE_SIZE) #define NEW_FW_MAX_SIZE 196608 +#define FLASH_BUFFER 0x450000 +#define FLASH_BUFFER_END 0x480000 + +#define NN_VERIFICATION_LEN 8 #endif /* FLASH_H_ */ \ No newline at end of file diff --git a/ZodiacFX/src/http.c b/ZodiacFX/src/http.c index 27d5d00..fca726f 100644 --- a/ZodiacFX/src/http.c +++ b/ZodiacFX/src/http.c @@ -53,6 +53,7 @@ extern uint32_t uid_buf[4]; // Unique identifier extern struct tcp_pcb *tcp_pcb; extern int OF_Version; extern uint8_t shared_buffer[SHARED_BUFFER_LEN]; // SHARED_BUFFER_LEN must never be reduced below 2048 +extern int tcp_con_state; // Check connection state extern struct ofp_flow_mod *flow_match10[MAX_FLOWS_10]; extern struct ofp13_flow_mod *flow_match13[MAX_FLOWS_13]; @@ -61,7 +62,10 @@ extern uint8_t *ofp13_oxm_inst[MAX_FLOWS_13]; extern uint16_t ofp13_oxm_inst_size[MAX_FLOWS_13]; extern struct flows_counter flow_counters[MAX_FLOWS_13]; extern struct flow_tbl_actions *flow_actions10[MAX_FLOWS_13]; +extern struct meter_entry13 *meter_entry[MAX_METER_13]; +extern struct meter_band_stats_array band_stats_array[MAX_METER_13]; extern int iLastFlow; +extern int iLastMeter; extern struct ofp10_port_stats phys10_port_stats[4]; extern struct ofp13_port_stats phys13_port_stats[4]; extern struct table_counter table_counters[MAX_TABLES]; @@ -71,31 +75,64 @@ extern int flash_write_page(uint8_t *flash_page); // Local Variables struct tcp_pcb *http_pcb; -char http_msg[64]; // Buffer for HTTP message filtering +static char http_msg[64]; // Buffer for HTTP message filtering +static char post_msg[64]; // Buffer for HTTP message filtering +static int page_ctr = 1; +static int boundary_start = 1; // Check for start of data +static uint8_t flowBase = 0; // Current set of flows to display +static uint8_t meterBase = 0; // Current set of meters to display +static struct tcp_pcb * upload_pcb; // Firmware upload connection check (pcb pointer) +static int upload_port = 0; +static int upload_timer = 0; // Timer for firmware upload timeout +static struct http_conns http_conn[MAX_CONN]; // http connection status + +// Flag variables +bool restart_required_outer = false; +static bool restart_required = false; // Track if any configuration changes are pending a restart static bool file_upload = false; // Multi-part firmware file upload flag -bool reset_required; +static bool post_pending = false; static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); static err_t http_accept(void *arg, struct tcp_pcb *pcb, err_t err); void http_send(char *buffer, struct tcp_pcb *pcb, bool out); +static err_t http_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); +void http_close(struct tcp_pcb *pcb); + +static uint8_t upload_handler(char *payload, int len); +// HTML resources static uint8_t interfaceCreate_Frames(void); static uint8_t interfaceCreate_Header(void); static uint8_t interfaceCreate_Menu(void); static uint8_t interfaceCreate_Home(void); static uint8_t interfaceCreate_Upload(void); +static uint8_t interfaceCreate_Upload_Status(uint8_t sel); static uint8_t interfaceCreate_Display_Home(void); static uint8_t interfaceCreate_Display_Ports(uint8_t step); static uint8_t interfaceCreate_Display_OpenFlow(void); static uint8_t interfaceCreate_Display_Flows(void); +static uint8_t interfaceCreate_Display_Meters(void); static uint8_t interfaceCreate_Config_Home(void); static uint8_t interfaceCreate_Config_Network(void); static uint8_t interfaceCreate_Config_VLANs(void); static uint8_t interfaceCreate_Config_OpenFlow(void); static uint8_t interfaceCreate_About(void); +static uint8_t interfaceCreate_Restart(void); -static uint8_t upload_handler(char *ppart, int len); -static int page_ctr = 1; +static uint8_t http_header[] = "HTTP/1.1 200 OK\r\n"\ + "Connection: Keep-Alive\r\n"\ + "Content-Type: text/html; charset=UTF-8\r\n\r\n"; + +static uint8_t html_style_body[] = "body {"\ + "overflow: auto;"\ + "font-family:Sans-serif;"\ + "line-height: 1.2em;"\ + "font-size: 17px;"\ + "margin-left: 20px;"\ + "}"; + +// Configuration functions +static uint8_t Config_Network(char *payload, int len); /* @@ -133,15 +170,65 @@ static err_t http_accept(void *arg, struct tcp_pcb *pcb, err_t err) tcp_recv(pcb, http_recv); tcp_err(pcb, NULL); tcp_poll(pcb, NULL, 4); + tcp_sent(pcb, http_sent); return ERR_OK; } +/* +* HTTP Sent callback function +* +* @param *arg - pointer the additional TCP args +* @param *tcp_pcb - pointer the TCP session structure. +* +*/ +static err_t http_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len) +{ + TRACE("http.c: [http_sent] %d bytes sent", len); + for(int i=0; i 3000) // 3s connection timeout + { + TRACE("http.c: pcb 0x%08x has timed out. Connection will be closed.", http_conn[i].attached_pcb); + http_close(http_conn[i].attached_pcb); + } + } + } + if(restart_required == true) + { + restart_required_outer = true; + //TRACE("http.c: restarting the Zodiac FX. Please reconnect."); + //for(int x = 0;x<100000;x++); // Let the above message get sent to the terminal before detaching + //udc_detach(); // Detach the USB device before restart + //rstc_start_software_reset(RSTC); // Software reset + //while (1); + } + + return ERR_OK; +} + /* * HTTP receive function * */ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) -{ +{ + // Local variables int len; int i = 0; char *http_payload; @@ -154,17 +241,117 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err http_payload = (char*)p->payload; len = p->tot_len; + TRACE("http.c: -- HTTP recv received %d/%d payload bytes in this pbuf", p->len, p->tot_len); + TRACE("http.c: -> pcb @ addr: 0x%08x, remote port %d", pcb, pcb->remote_port); + if(file_upload == true) - { + { + TRACE("http.c: %d ms since last firmware packet received", (sys_get_ms() - upload_timer)); + + // Check upload timeout + if(upload_timer != 0 && sys_get_ms() - upload_timer > UPLOAD_TIMEOUT) + { + TRACE("http.c: firmware upload has timed out"); + + /* Header request check */ + memset(&http_msg, 0, sizeof(http_msg)); // Clear HTTP message array + + // Specified resource directly follows GET + i = 0; + while(i < 63 && (http_payload[i+5] != ' ')) + { + http_msg[i] = http_payload[i+5]; // Offset http_payload to isolate resource + i++; + } + + // The "upload failed" message does not need to show up in the header + if(strcmp(http_msg,"header.htm") != 0) + { + // Stop upload operation + upload_handler(NULL, 0); // Clean up upload operation + if(interfaceCreate_Upload_Status(2)) + { + http_send(&shared_buffer, pcb, 1); + TRACE("http.c: Page sent successfully - %d bytes", strlen(shared_buffer)); + } + else + { + TRACE("http.c: Unable to serve page - buffer at %d bytes", strlen(shared_buffer)); + } + } + } + + if(upload_pcb != pcb && upload_port != pcb->remote_port) + { + TRACE("http.c: incoming connection ignored - upload currently in progress"); + + /* Header request check */ + memset(&http_msg, 0, sizeof(http_msg)); // Clear HTTP message array + + // Specified resource directly follows GET + i = 0; + while(i < 63 && (http_payload[i+5] != ' ')) + { + http_msg[i] = http_payload[i+5]; // Offset http_payload to isolate resource + i++; + } + + // The "upload in progress" message does not need to show up in the header + if(strcmp(http_msg,"header.htm") != 0) + { + if(interfaceCreate_Upload_Status(4)) + { + http_send(&shared_buffer, pcb, 1); + TRACE("http.c: Page sent successfully - %d bytes", strlen(shared_buffer)); + } + else + { + TRACE("http.c: Unable to serve page - buffer at %d bytes", strlen(shared_buffer)); + } + } + + return ERR_OK; + } + + // Update timer value (new firmware packet received) + upload_timer = sys_get_ms(); + int ret = 0; - int tst = 3000; // Handle multi-part file data ret = upload_handler(http_payload, len); - while(tst) + if(ret == 2) { - tst--; + file_upload = false; + boundary_start = 1; + //flash_clear_gpnvm(1); + // upload check + if(verification_check() == SUCCESS) + { + upload_handler(NULL, 0); // Clean up upload operation + if(interfaceCreate_Upload_Status(1)) + { + http_send(&shared_buffer, pcb, 1); + TRACE("http.c: Page sent successfully - %d bytes", strlen(shared_buffer)); + } + else + { + TRACE("http.c: Unable to serve page - buffer at %d bytes", strlen(shared_buffer)); + } + } + else + { + upload_handler(NULL, 0); // Clean up upload operation + if(interfaceCreate_Upload_Status(3)) + { + http_send(&shared_buffer, pcb, 1); + TRACE("http.c: Page sent successfully - %d bytes", strlen(shared_buffer)); + } + else + { + TRACE("http.c: Unable to serve page - buffer at %d bytes", strlen(shared_buffer)); + } + } } - tst = 0; // _______________________________ for debug purposes } else { @@ -175,10 +362,10 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err http_msg[i] = http_payload[i]; i++; } - TRACE("http.c: %s method received", http_msg); if(strcmp(http_msg,"GET") == 0) { + TRACE("http.c: GET method received"); memset(&http_msg, 0, sizeof(http_msg)); // Clear HTTP message array // Specified resource directly follows GET @@ -199,7 +386,7 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err } // Check resource & serve page - if(http_msg[0] == '\0') + if(http_msg[0] == '\0' || strcmp(http_msg,"frames.html") == 0) { if(interfaceCreate_Frames()) { @@ -318,6 +505,18 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err TRACE("http.c: Unable to serve page - buffer at %d bytes", strlen(shared_buffer)); } } + else if(strcmp(http_msg,"d_meters.htm") == 0) + { + if(interfaceCreate_Display_Meters()) + { + http_send(&shared_buffer, pcb, 1); + TRACE("http.c: Page sent successfully - %d bytes", strlen(shared_buffer)); + } + else + { + TRACE("http.c: Unable to serve page - buffer at %d bytes", strlen(shared_buffer)); + } + } else if(strcmp(http_msg,"cfg_home.htm") == 0) { if(interfaceCreate_Config_Home()) @@ -383,8 +582,10 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err TRACE("http.c: resource doesn't exist:\"%s\"", http_msg); } } - else if(strcmp(http_msg,"POST") == 0) + + else if(strcmp(http_msg,"POST") == 0 && post_pending == false) { + TRACE("http.c: POST method received"); memset(&http_msg, 0, sizeof(http_msg)); // Clear HTTP message array // Specified resource directly follows POST @@ -394,12 +595,24 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err http_msg[i] = http_payload[i+6]; // Offset http_payload to isolate resource i++; } + memcpy(post_msg, http_msg, 64); + TRACE("http.c: request for %s", post_msg); + post_pending = true; + pbuf_free(p); + return ERR_OK; + } + else + { + TRACE("http.c: unknown HTTP method received"); + } - TRACE("http.c: request for %s", http_msg); - - if(strcmp(http_msg,"upload") == 0) + + if(post_pending == true) + { + post_pending = false; + if(strcmp(post_msg,"upload") == 0) { - // Initialise flash programming + // Initialize flash programming if(firmware_update_init()) { TRACE("http.c: firmware update initialisation successful"); @@ -411,223 +624,42 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err // All following packets will contain multi-part file data file_upload = true; + // Store pcb pointer value for this connection + upload_pcb = pcb; + // Store remote port + upload_port = pcb->remote_port; + // Initialize timeout value + upload_timer = sys_get_ms(); + + upload_handler(http_payload, len); } - else if(strcmp(http_msg,"save_config") == 0) + else if(strcmp(post_msg,"save_config") == 0) { - memset(&http_msg, 0, sizeof(http_msg)); // Clear HTTP message array - - // Device Name - pdat = strstr(http_payload, "wi_deviceName"); // Search for element - if(pdat != NULL) // Check that element exists - { - pdat += (strlen("wi_deviceName")+1); // Data format: wi_deviceName=(name) - - i = 0; - while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) - { - http_msg[i] = pdat[i]; // Store value of element - i++; - } - if(pdat[i+1] == 'w') // Check that the next parameter directly follows the "&" at end of data - { - uint8_t namelen = strlen(http_msg); - if (namelen > 15 ) namelen = 15; // Make sure name is less than 16 characters - sprintf(Zodiac_Config.device_name, http_msg, namelen); - TRACE("http.c: device name set to '%s'",Zodiac_Config.device_name); - } - else - { - TRACE("http.c: \"&\" cannot be used in device name"); - } - } - else - { - TRACE("http.c: no device name found"); - } - - memset(&http_msg, 0, sizeof(http_msg)); - - // MAC Address - pdat = strstr(http_payload, "wi_macAddress"); - if(pdat != NULL) // Check that element exists - { - pdat += (strlen("wi_macAddress")+1); // Data format: wi_deviceName=(name) - - i = 0; - while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) - { - http_msg[i] = pdat[i]; // Store value of element - i++; - } - if(pdat[i+1] == 'w') - { - int mac1,mac2,mac3,mac4,mac5,mac6; - char decArr[18] = ""; - int j, k; - - if (strlen(http_msg) != 27 ) // Accounting for ":" as "%3A" - { - TRACE("http.c: incorrect MAC address format"); - return; - } - - // Decode http string - j = 0; k = 0; - while(j < strlen(http_msg) && k < 18) - { - if(http_msg[j] == '%' && http_msg[j+1] == '3' && http_msg[j+2] == 'A') - { - decArr[k] = ':'; - j+=3; k++; - } - else - { - decArr[k] = http_msg[j]; - j++; k++; - } - } - - sscanf(decArr, "%x:%x:%x:%x:%x:%x", &mac1, &mac2, &mac3, &mac4, &mac5, &mac6); - Zodiac_Config.MAC_address[0] = mac1; - Zodiac_Config.MAC_address[1] = mac2; - Zodiac_Config.MAC_address[2] = mac3; - Zodiac_Config.MAC_address[3] = mac4; - Zodiac_Config.MAC_address[4] = mac5; - Zodiac_Config.MAC_address[5] = mac6; - TRACE("http.c: MAC address set to %.2X:%.2X:%.2X:%.2X:%.2X:%.2X",Zodiac_Config.MAC_address[0], Zodiac_Config.MAC_address[1], Zodiac_Config.MAC_address[2], Zodiac_Config.MAC_address[3], Zodiac_Config.MAC_address[4], Zodiac_Config.MAC_address[5]); - } - else - { - TRACE("http.c: \"&\" cannot be used in form"); - } - } - else - { - TRACE("http.c: no MAC address found"); - } - - memset(&http_msg, 0, sizeof(http_msg)); - - // IP Address - pdat = strstr(http_payload, "wi_ipAddress"); - if(pdat != NULL) // Check that element exists - { - pdat += (strlen("wi_ipAddress")+1); // Data format: wi_deviceName=(name) - - i = 0; - while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) - { - http_msg[i] = pdat[i]; // Store value of element - i++; - } - if(pdat[i+1] == 'w') - { - int ip1,ip2,ip3,ip4; - if (strlen(http_msg) > 15 ) - { - TRACE("http.c: incorrect IP format"); - return; - } - sscanf(http_msg, "%d.%d.%d.%d", &ip1, &ip2,&ip3,&ip4); - Zodiac_Config.IP_address[0] = ip1; - Zodiac_Config.IP_address[1] = ip2; - Zodiac_Config.IP_address[2] = ip3; - Zodiac_Config.IP_address[3] = ip4; - TRACE("http.c: IP address set to %d.%d.%d.%d" , Zodiac_Config.IP_address[0], Zodiac_Config.IP_address[1], Zodiac_Config.IP_address[2], Zodiac_Config.IP_address[3]); - } - else - { - TRACE("http.c: \"&\" cannot be used in form"); - } - } - else - { - TRACE("http.c: no IP address found"); - } - - memset(&http_msg, 0, sizeof(http_msg)); - - // Netmask - pdat = strstr(http_payload, "wi_netmask"); - if(pdat != NULL) // Check that element exists - { - pdat += (strlen("wi_netmask")+1); // Data format: wi_deviceName=(name) - - i = 0; - while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) - { - http_msg[i] = pdat[i]; // Store value of element - i++; - } - if(pdat[i+1] == 'w') - { - int nm1,nm2,nm3,nm4; - if (strlen(http_msg) > 15 ) - { - TRACE("http.c: incorrect netmask format"); - return; - } - sscanf(http_msg, "%d.%d.%d.%d", &nm1, &nm2,&nm3,&nm4); - Zodiac_Config.netmask[0] = nm1; - Zodiac_Config.netmask[1] = nm2; - Zodiac_Config.netmask[2] = nm3; - Zodiac_Config.netmask[3] = nm4; - TRACE("http.c: netmask set to %d.%d.%d.%d" , Zodiac_Config.netmask[0], Zodiac_Config.netmask[1], Zodiac_Config.netmask[2], Zodiac_Config.netmask[3]); - } - else - { - TRACE("http.c: \"&\" cannot be used in form"); - } - } - else + if(Config_Network(http_payload, len) == SUCCESS) { - TRACE("http.c: no netmask found"); - } - - memset(&http_msg, 0, sizeof(http_msg)); + TRACE("http.c: network configuration successful"); - // Gateway - pdat = strstr(http_payload, "wi_gateway"); - if(pdat != NULL) // Check that element exists - { - pdat += (strlen("wi_gateway")+1); // Data format: wi_deviceName=(name) - - i = 0; - while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) + // Send updated config page + if(interfaceCreate_Config_Network()) { - http_msg[i] = pdat[i]; // Store value of element - i++; + http_send(&shared_buffer, pcb, 1); + TRACE("http.c: updated page sent successfully - %d bytes", strlen(shared_buffer)); + return SUCCESS; } - - // No next 'w' character check as this is the last element - - int gw1,gw2,gw3,gw4; - if (strlen(http_msg) > 15 ) + else { - TRACE("http.c: incorrect gateway format"); - return; + TRACE("http.c: unable to serve updated page - buffer at %d bytes", strlen(shared_buffer)); + return FAILURE; } - sscanf(http_msg, "%d.%d.%d.%d", &gw1, &gw2,&gw3,&gw4); - Zodiac_Config.gateway_address[0] = gw1; - Zodiac_Config.gateway_address[1] = gw2; - Zodiac_Config.gateway_address[2] = gw3; - Zodiac_Config.gateway_address[3] = gw4; - TRACE("http.c: gateway set to %d.%d.%d.%d" , Zodiac_Config.gateway_address[0], Zodiac_Config.gateway_address[1], Zodiac_Config.gateway_address[2], Zodiac_Config.gateway_address[3]); } else { - TRACE("http.c: no gateway address found"); + TRACE("http.c: ERROR: network configuration failed"); } - - // Save configuration to EEPROM - eeprom_write(); - TRACE("http.c: config written to EEPROM"); - - // Set update required flag - reset_required = true; - - // Send updated config page - if(interfaceCreate_Config_Network()) + } + else if(strcmp(post_msg,"btn_restart") == 0) + { + if(interfaceCreate_Restart()) { http_send(&shared_buffer, pcb, 1); TRACE("http.c: updated page sent successfully - %d bytes", strlen(shared_buffer)); @@ -636,24 +668,9 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err { TRACE("http.c: unable to serve updated page - buffer at %d bytes", strlen(shared_buffer)); } - - // Send updated header page (with restart button) - - // ***** Placeholder until frame refresh targeting is implemented - // - // - // - + restart_required = true; } - else if(strcmp(http_msg,"btn_restart") == 0) - { - TRACE("http.c: restarting the Zodiac FX. Please reconnect."); - for(int x = 0;x<100000;x++); // Let the above message get sent to the terminal before detaching - udc_detach(); // Detach the USB device before restart - rstc_start_software_reset(RSTC); // Software reset - while (1); - } - else if(strcmp(http_msg,"btn_default") == 0) + else if(strcmp(post_msg,"btn_default") == 0) { TRACE("http.c: restoring factory settings"); @@ -710,7 +727,7 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err rstc_start_software_reset(RSTC); // Software reset while (1); } - else if(strcmp(http_msg,"save_ports") == 0) + else if(strcmp(post_msg,"save_ports") == 0) { // Save VLAN port associations @@ -820,16 +837,59 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err TRACE("http.c: unable to serve updated page - buffer at %d bytes", strlen(shared_buffer)); } } - else if(strcmp(http_msg,"btn_ofNext") == 0) + else if(strcmp(post_msg,"btn_ofPage") == 0) { - - } - else if(strcmp(http_msg,"btn_ofPrev") == 0) - { - + // Display: Flows, Previous and Next flow page buttons + + if(strstr(http_payload, "btn_ofNext") != NULL) // Check that element exists + { + TRACE("http.c: request for next page of flows"); + TRACE("http.c: current flowBase: %d; current iLastFlow: %d;", flowBase, iLastFlow) + if(flowBase < iLastFlow-FLOW_DISPLAY_LIMIT) + { + // Increment flow base (display next set on page send) + flowBase += FLOW_DISPLAY_LIMIT; + TRACE("http.c: new flowBase: %d; current iLastFlow: %d;", flowBase, iLastFlow) + } + else + { + TRACE("http.c: flowBase already reaches end - NOT incremented") + } + } + else if(strstr(http_payload, "btn_ofPrev") != NULL) + { + TRACE("http.c: request for previous page of flows"); + TRACE("http.c: current flowBase: %d; current iLastFlow: %d;", flowBase, iLastFlow) + if(flowBase >= FLOW_DISPLAY_LIMIT) + { + // Decrement flow base (display previous set on page send) + flowBase -= FLOW_DISPLAY_LIMIT; + TRACE("http.c: new flowBase: %d; current iLastFlow: %d;", flowBase, iLastFlow) + } + else + { + TRACE("http.c: flowBase already at start - NOT decremented") + } + } + else + { + TRACE("http.c: ERROR: invalid request"); + } + + // Send updated page + if(interfaceCreate_Display_Flows()) + { + http_send(&shared_buffer, pcb, 1); + TRACE("http.c: updated page sent successfully - %d bytes", strlen(shared_buffer)); + } + else + { + TRACE("http.c: unable to serve updated page - buffer at %d bytes", strlen(shared_buffer)); + } } - else if(strcmp(http_msg,"btn_ofClear") == 0) + else if(strcmp(post_msg,"btn_ofClear") == 0) { + // Display: Flows // Clear the flow table TRACE("http.c: clearing flow table, %d flow deleted.\r\n", iLastFlow); clear_flows(); @@ -845,8 +905,60 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err TRACE("http.c: unable to serve updated page - buffer at %d bytes", strlen(shared_buffer)); } } - else if(strcmp(http_msg,"save_vlan") == 0) + else if(strcmp(post_msg,"btn_meterPage") == 0) + { + // Display: Meters, Previous and Next meter page buttons + + if(strstr(http_payload, "btn_meterNext") != NULL) // Check that element exists + { + TRACE("http.c: request for next page of meters"); + TRACE("http.c: current meterBase: %d; current iLastMeter: %d;", meterBase, iLastMeter) + if(meterBase < iLastMeter-METER_DISPLAY_LIMIT) + { + // Increment flow base (display next set on page send) + meterBase += METER_DISPLAY_LIMIT; + TRACE("http.c: new meterBase: %d; current iLastMeter: %d;", meterBase, iLastMeter) + } + else + { + TRACE("http.c: meterBase already reaches end - NOT incremented") + } + } + else if(strstr(http_payload, "btn_meterPrev") != NULL) + { + TRACE("http.c: request for previous page of meters"); + TRACE("http.c: current meterBase: %d; current iLastMeter: %d;", meterBase, iLastMeter) + if(meterBase >= METER_DISPLAY_LIMIT) + { + // Decrement meter base (display previous set on page send) + meterBase -= METER_DISPLAY_LIMIT; + TRACE("http.c: new meterBase: %d; current iLastMeter: %d;", meterBase, iLastMeter) + } + else + { + TRACE("http.c: meterBase already at start - NOT decremented") + } + } + else + { + TRACE("http.c: ERROR: invalid request"); + } + + // Send updated page + if(interfaceCreate_Display_Meters()) + { + http_send(&shared_buffer, pcb, 1); + TRACE("http.c: updated page sent successfully - %d bytes", strlen(shared_buffer)); + } + else + { + TRACE("http.c: unable to serve updated page - buffer at %d bytes", strlen(shared_buffer)); + } + } + else if(strcmp(post_msg,"save_vlan") == 0) { + // Config: VLANs, Add and Delete buttons + memset(&http_msg, 0, sizeof(http_msg)); // Clear HTTP message array // Search for btn= @@ -1024,8 +1136,10 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err TRACE("http.c: unable to serve updated page - buffer at %d bytes", strlen(shared_buffer)); } } - else if(strcmp(http_msg,"save_of") == 0) + else if(strcmp(post_msg,"save_of") == 0) { + // Config: OpenFlow, Save OpenFlow configuration + // Controller IP Address memset(&http_msg, 0, sizeof(http_msg)); pdat = strstr(http_payload, "wi_ofIP"); @@ -1194,10 +1308,14 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err TRACE("http.c: unknown request: \"%s\"", http_msg); } } - else - { - TRACE("http.c: WARNING: unknown HTTP method received"); - } + + } + } + else + { + if(err != ERR_OK) + { + TRACE("http.c: receive error - %d", err); } } @@ -1208,14 +1326,13 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err TRACE("http.c: Closing TCP connection."); tcp_close(pcb); } - return ERR_OK; } /* * HTTP Send function * -* Parameter: +* Parameters: * out - specify whether TCP packet should be sent */ void http_send(char *buffer, struct tcp_pcb *pcb, bool out) @@ -1231,208 +1348,705 @@ void http_send(char *buffer, struct tcp_pcb *pcb, bool out) { // Write data to tcp buffer err = tcp_write(pcb, buffer, len, TCP_WRITE_FLAG_COPY + TCP_WRITE_FLAG_MORE); - TRACE("http.c: sending %d bytes to TCP stack, %d REMAINING in buffer", len, (buf_size - len)); + TRACE("http.c: tcp buffer %d/%d", len, buf_size); - // Check if more data needs to be written - if(out == true) + // Check if data is a part of a larger write + for(int i=0; i0) { py--; - if((*py) == '\x0d' && (*(py+1)) == '\x0a' && (*(py+2)) == '\x2d' && (*(py+3)) == '\x2d') + // Latch onto '----' ("----[boundary ID]") + if((*(py-1)) == '\x2d' && (*(py-2)) == '\x2d' && (*(py-3)) == '\x2d' && (*(py-4)) == '\x2d') { - i = 0; - // 'i' will be decremented to -1 if this line is run + // Store the discovered boundary + char tmpID[BOUNDARY_MAX_LEN] = {0}; + int z = 0; + while(z < BOUNDARY_MAX_LEN && *(py+z) != '\x2d' && *(py+z) != '\x0d' && *(py+z) != '\x0a') + { + tmpID[z] = *(py+z); + z++; + } + + TRACE("http.c: discovered boundary ID : %s", tmpID); + + // Match the boundary ID with stored ID + if(strcmp(tmpID, boundary_ID) == 0) + { + TRACE("http.c: boundary IDs match"); + TRACE("http.c: moving data end pointer"); + // Traverse through the preceding newline characters + while(*(py-1) == '\x0d' || *(py-1) == '\x0a' || *(py-1) == '\x2d') + { + py--; + } + + i = 0; + // 'i' will be decremented to -1 if this line is run + } + else + { + TRACE("http.c: boundary IDs do not match"); + i = 1; + // 'i' will be decremented to 0 if this line is run + } } i--; } + if(i == 0) { TRACE("http.c: ending boundary not found - ending data is valid"); + + // Return ending pointer to the end + py = payload + len; } else { TRACE("http.c: ending boundary found"); - - // Return ending pointer to the end - py = ppart + len; - final = 1; } - // Write data + // Get length of uploaded part + data_len = py - px; + + // Check if any existing data needs to be handled if(saved_bytes) { - // Fill in unwritten page (if it exists) + TRACE("http.c: %d saved bytes need to be cleared", saved_bytes); + if(final) { - while(saved_bytes < 512 && handled_bytes < len) + /* Final page needs to be written */ + + // Fill 512-byte array + while(saved_bytes < IFLASH_PAGE_SIZE) { - page[saved_bytes] = *px; - px++; + if(px < py) + { + // Write data + page[saved_bytes] = *px; + px++; + handled_bytes++; + } + else + { + // Append 0xFF + page[saved_bytes] = 0xFF; + } + saved_bytes++; - handled_bytes++; + } + + // Write data to page + if(flash_write_page(&page)) + { + TRACE("http.c: final firmware page written successfully"); + page_ctr++; + } + else + { + TRACE("http.c: final firmware page write FAILED"); } } - else + else if(saved_bytes + len < IFLASH_PAGE_SIZE) { - while(saved_bytes < 512) + int max_len = saved_bytes + len; + // Fill existing partially-complete page with new data + while(saved_bytes < max_len && handled_bytes < len) { page[saved_bytes] = *px; - px++; + if(px < py) + { + px++; + } + else + { + TRACE("http.c: ERROR - multi-part start pointer has passed the end pointer"); + } saved_bytes++; handled_bytes++; } - } - - // Write data to page - if(flash_write_page(&page)) // ___________________ CHECK - { - TRACE("http.c: firmware page written successfully (%02d)", page_ctr); - page_ctr++; + + // Handle edge-case + TRACE("http.c: unable to fill a complete page - skipping page write"); + TRACE("http.c: %d bytes saved", saved_bytes); + + total_handled_bytes += handled_bytes; + return 1; } else { - TRACE("http.c: firmware page write FAILED (%02d)", page_ctr); + // Fill existing partially-complete page with new data + while(saved_bytes < IFLASH_PAGE_SIZE && handled_bytes < len) + { + page[saved_bytes] = *px; + if(px < py) + { + px++; + } + else + { + TRACE("http.c: ERROR - multi-part start pointer has passed the end pointer"); + } + saved_bytes++; + handled_bytes++; + } + + // Write data to page + if(flash_write_page(&page)) + { + TRACE("http.c: firmware page written successfully"); + page_ctr++; + } + else + { + TRACE("http.c: firmware page write FAILED"); + } } - - memset(&page, 0, 512); + + // Saved bytes have been handled - clear the counter saved_bytes = 0; + + TRACE("http.c: saved bytes have been cleared"); + TRACE("http.c: handled_bytes: %04d, data_len: %04d", handled_bytes, data_len); } - - if(handled_bytes < len) - { - int j; - // Check for final page of data - if(final) + while(handled_bytes < data_len) + { + if(data_len - handled_bytes >= IFLASH_PAGE_SIZE) { - j = 0; - while(px < py) + // Fill 512-byte array + int j = 0; + while(j < IFLASH_PAGE_SIZE) { page[j] = *px; - px++; + if(px < py) + { + px++; + } + else + { + TRACE("http.c: ERROR - multi-part start pointer has passed the end pointer"); + } j++; handled_bytes++; } + + // Write to page + if(flash_write_page(&page)) + { + TRACE("http.c: firmware page written successfully"); + page_ctr++; + } + else + { + TRACE("http.c: firmware page write FAILED"); + } } - - // Write full pages - while(len - handled_bytes >= 512) + else if(!final) { - j = 0; - while(j < 512) + /* Data needs to be saved */ + TRACE("http.c: data needs to be saved"); + + // Save leftover into page array for next run-through + int j = 0; + while(handled_bytes < data_len) { - page[j] = *px; // Store value of element - px++; + page[j] = *px; + if(px < py) + { + px++; + } + else + { + TRACE("http.c: ERROR - multi-part start pointer has passed the end pointer"); + } j++; handled_bytes++; + saved_bytes++; } - - // Write data to page - if(flash_write_page(&page)) // ___________________ CHECK + + TRACE("http.c: %d bytes saved", saved_bytes); + } + else + { + /* Final page needs to be written */ + + // Fill 512-byte array + int j = 0; + while(j < IFLASH_PAGE_SIZE) + { + if(px < py) + { + // Write data + page[j] = *px; + px++; + handled_bytes++; + } + else + { + // Append 0xFF + page[j] = 0xFF; + } + + j++; + } + + // Write to page + if(flash_write_page(&page)) { - TRACE("http.c: firmware page written successfully (%02d)", page_ctr); + TRACE("http.c: final page written successfully"); page_ctr++; } else { - TRACE("http.c: firmware page write FAILED (%02d)", page_ctr); + TRACE("http.c: final page write FAILED"); + } + } + + TRACE("http.c: handled_bytes: %04d, data_len: %04d", handled_bytes, data_len); + } + + total_handled_bytes += handled_bytes; + TRACE("http.c: total_handled_bytes: %d", total_handled_bytes); + + if(final) + { + return 2; + } + else + { + return 1; + } +} + +static uint8_t Config_Network(char *payload, int len) +{ + int i = 0; + char *pdat; + payload[len] = '&'; + + memset(&http_msg, 0, sizeof(http_msg)); // Clear HTTP message array + + // Device Name + pdat = strstr(payload, "wi_deviceName"); // Search for element + if(pdat != NULL) // Check that element exists + { + pdat += (strlen("wi_deviceName")+1); // Data format: wi_deviceName=(name) + + i = 0; + while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) + { + http_msg[i] = pdat[i]; // Store value of element + i++; + } + if(pdat[i+1] == 'w') // Check that the next parameter directly follows the "&" at end of data + { + uint8_t namelen = strlen(http_msg); + if (namelen > 15 ) namelen = 15; // Make sure name is less than 16 characters + sprintf(Zodiac_Config.device_name, http_msg, namelen); + TRACE("http.c: device name set to '%s'",Zodiac_Config.device_name); + } + else + { + TRACE("http.c: \"&\" cannot be used in device name"); + } + } + else + { + TRACE("http.c: no device name found"); + } + + memset(&http_msg, 0, sizeof(http_msg)); + + // MAC Address + pdat = strstr(payload, "wi_macAddress"); + if(pdat != NULL) // Check that element exists + { + pdat += (strlen("wi_macAddress")+1); // Data format: wi_deviceName=(name) + + i = 0; + while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) + { + http_msg[i] = pdat[i]; // Store value of element + i++; + } + if(pdat[i+1] == 'w') + { + int mac1,mac2,mac3,mac4,mac5,mac6; + char decArr[18] = ""; + int j, k; + + if (strlen(http_msg) != 27 ) // Accounting for ":" as "%3A" + { + TRACE("http.c: incorrect MAC address format"); + return; + } + + // Decode http string + j = 0; k = 0; + while(j < strlen(http_msg) && k < 18) + { + if(http_msg[j] == '%' && http_msg[j+1] == '3' && http_msg[j+2] == 'A') + { + decArr[k] = ':'; + j+=3; k++; + } + else + { + decArr[k] = http_msg[j]; + j++; k++; + } } - memset(&page, 0, 512); + sscanf(decArr, "%x:%x:%x:%x:%x:%x", &mac1, &mac2, &mac3, &mac4, &mac5, &mac6); + Zodiac_Config.MAC_address[0] = mac1; + Zodiac_Config.MAC_address[1] = mac2; + Zodiac_Config.MAC_address[2] = mac3; + Zodiac_Config.MAC_address[3] = mac4; + Zodiac_Config.MAC_address[4] = mac5; + Zodiac_Config.MAC_address[5] = mac6; + TRACE("http.c: MAC address set to %.2X:%.2X:%.2X:%.2X:%.2X:%.2X",Zodiac_Config.MAC_address[0], Zodiac_Config.MAC_address[1], Zodiac_Config.MAC_address[2], Zodiac_Config.MAC_address[3], Zodiac_Config.MAC_address[4], Zodiac_Config.MAC_address[5]); } + else + { + TRACE("http.c: \"&\" cannot be used in form"); + } + } + else + { + TRACE("http.c: no MAC address found"); + } + + memset(&http_msg, 0, sizeof(http_msg)); + + // IP Address + pdat = strstr(payload, "wi_ipAddress"); + if(pdat != NULL) // Check that element exists + { + pdat += (strlen("wi_ipAddress")+1); // Data format: wi_deviceName=(name) - // Save unwritten data - j = 0; - while(handled_bytes < len) + i = 0; + while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) { - page[j] = *px; // Store value of element - px++; - j++; - handled_bytes++; + http_msg[i] = pdat[i]; // Store value of element + i++; + } + if(pdat[i+1] == 'w') + { + int ip1,ip2,ip3,ip4; + if (strlen(http_msg) > 15 ) + { + TRACE("http.c: incorrect IP format"); + return; + } + sscanf(http_msg, "%d.%d.%d.%d", &ip1, &ip2,&ip3,&ip4); + Zodiac_Config.IP_address[0] = ip1; + Zodiac_Config.IP_address[1] = ip2; + Zodiac_Config.IP_address[2] = ip3; + Zodiac_Config.IP_address[3] = ip4; + TRACE("http.c: IP address set to %d.%d.%d.%d" , Zodiac_Config.IP_address[0], Zodiac_Config.IP_address[1], Zodiac_Config.IP_address[2], Zodiac_Config.IP_address[3]); + } + else + { + TRACE("http.c: \"&\" cannot be used in form"); } + } + else + { + TRACE("http.c: no IP address found"); + } + + memset(&http_msg, 0, sizeof(http_msg)); + + // Netmask + pdat = strstr(payload, "wi_netmask"); + if(pdat != NULL) // Check that element exists + { + pdat += (strlen("wi_netmask")+1); // Data format: wi_deviceName=(name) - if(px > py) + i = 0; + while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) { - TRACE("http.c: ERROR - pointer has passed the data"); - return 0; + http_msg[i] = pdat[i]; // Store value of element + i++; + } + if(pdat[i+1] == 'w') + { + int nm1,nm2,nm3,nm4; + if (strlen(http_msg) > 15 ) + { + TRACE("http.c: incorrect netmask format"); + return; + } + sscanf(http_msg, "%d.%d.%d.%d", &nm1, &nm2,&nm3,&nm4); + Zodiac_Config.netmask[0] = nm1; + Zodiac_Config.netmask[1] = nm2; + Zodiac_Config.netmask[2] = nm3; + Zodiac_Config.netmask[3] = nm4; + TRACE("http.c: netmask set to %d.%d.%d.%d" , Zodiac_Config.netmask[0], Zodiac_Config.netmask[1], Zodiac_Config.netmask[2], Zodiac_Config.netmask[3]); + } + else + { + TRACE("http.c: \"&\" cannot be used in form"); } } + else + { + TRACE("http.c: no netmask found"); + } - if(final) + memset(&http_msg, 0, sizeof(http_msg)); + + // Gateway + pdat = strstr(payload, "wi_gateway"); + if(pdat != NULL) // Check that element exists { - return 2; + pdat += (strlen("wi_gateway")+1); // Data format: wi_deviceName=(name) + + i = 0; + while(i < 63 && (pdat[i] != '&') && (pdat[i] >= 31) && (pdat[i] <= 122)) + { + http_msg[i] = pdat[i]; // Store value of element + i++; + } + + // No next 'w' character check as this is the last element + + int gw1,gw2,gw3,gw4; + if (strlen(http_msg) > 15 ) + { + TRACE("http.c: incorrect gateway format"); + return; + } + sscanf(http_msg, "%d.%d.%d.%d", &gw1, &gw2,&gw3,&gw4); + Zodiac_Config.gateway_address[0] = gw1; + Zodiac_Config.gateway_address[1] = gw2; + Zodiac_Config.gateway_address[2] = gw3; + Zodiac_Config.gateway_address[3] = gw4; + TRACE("http.c: gateway set to %d.%d.%d.%d" , Zodiac_Config.gateway_address[0], Zodiac_Config.gateway_address[1], Zodiac_Config.gateway_address[2], Zodiac_Config.gateway_address[3]); } else { - return 1; + TRACE("http.c: no gateway address found"); } + + // Save configuration to EEPROM + eeprom_write(); + TRACE("http.c: config written to EEPROM"); + + return SUCCESS; + + // Send updated header page (with restart button) + + // ***** Placeholder until frame refresh targeting is implemented + // + // + // } /* @@ -1442,9 +2056,7 @@ static uint8_t upload_handler(char *ppart, int len) static uint8_t interfaceCreate_Frames(void) { // Format HTTP response - sprintf(shared_buffer,"HTTP/1.1 200 OK\r\n"); - strcat(shared_buffer,"Connection: close\r\n"); - strcat(shared_buffer,"Content-Type: text/html; charset=UTF-8\r\n\r\n"); + sprintf(shared_buffer, http_header); // Send frames strcat(shared_buffer, \ ""\ @@ -1489,68 +2101,13 @@ static uint8_t interfaceCreate_Frames(void) */ static uint8_t interfaceCreate_Header(void) { - reset_required = true; // ***** Placeholder until frame refresh targeting is implemented - int hr = (totaltime/2)/3600; int t = (totaltime/2)%3600; int min = t/60; // Send header - if(reset_required == false) - { - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ - ""\ - ""\ - ""\ - ""\ - ""\ - ""\ - ""\ - "
"\ - "

Zodiac FX

"\ - "
"\ - "
"\ - "Uptime: %02d:%02d"\ - "
"\ - ""\ - ""\ - , hr, min) < SHARED_BUFFER_LEN) - { - TRACE("http.c: html written to buffer"); - return 1; - } - else - { - TRACE("http.c: WARNING: html truncated to prevent buffer overflow"); - return 0; - } - } - else if(reset_required == true) - { - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + if( snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ""\ @@ -1593,7 +2150,7 @@ static uint8_t interfaceCreate_Header(void) "

Zodiac FX

"\ ""\ "
"\ - "
"\ + ""\ ""\ "
"\ "
"\ @@ -1613,7 +2170,6 @@ static uint8_t interfaceCreate_Header(void) return 0; } } -} /* * Create and format HTML for menu page @@ -1622,7 +2178,9 @@ static uint8_t interfaceCreate_Header(void) static uint8_t interfaceCreate_Menu(void) { // Send menu - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + + if( snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ""\ @@ -1656,11 +2214,12 @@ static uint8_t interfaceCreate_Menu(void) ""\ "
    "\ "
  • Status
  • "\ - //"
  • Update f/w
  • " + "
  • Update f/w
  • " "
  • Display
  • "\ "
  • Ports
  • "\ "
  • OpenFlow
  • "\ "
  • Flows
  • "\ + "
  • Meters
  • "\ "
  • Config
  • "\ "
  • Network
  • "\ "
  • VLANs
  • "\ @@ -1691,19 +2250,17 @@ static uint8_t interfaceCreate_Home(void) int t = (totaltime/2)%3600; int min = t/60; - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ @@ -1738,18 +2295,16 @@ static uint8_t interfaceCreate_Home(void) */ static uint8_t interfaceCreate_Upload(void) { - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ""\ ""\ ""\ ""\ @@ -1757,9 +2312,8 @@ static uint8_t interfaceCreate_Upload(void) "

    Firmware Update

    "\ "

    "\ ""\ - "

    Browser firmware update is currently only supported in Chrome.
    "\ - "Do not attempt an update with an unsupported browser.

    "\ - "
    "\ + "

    Browser firmware update supports official binaries (version 0.80 and later).

    Please find the latest version in the forums.

    "\ + ""\ "

    "\ ""\ "
    "\ @@ -1777,24 +2331,126 @@ static uint8_t interfaceCreate_Upload(void) } } +/* +* Create and format HTML for firmware update status page +* +*/ +static uint8_t interfaceCreate_Upload_Status(uint8_t sel) +{ + if(sel == 1) + { + if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + "

    "\ + "

    Firmware Update

    "\ + "

    "\ + ""\ + "

    Firmware upload successful.

    "\ + "Zodiac FX will be updated on the next restart.

    "\ + "
    "\ + ""\ + "
    "\ + ""\ + ""\ + ) < SHARED_BUFFER_LEN) + { + TRACE("http.c: html written to buffer"); + return 1; + } + else + { + TRACE("http.c: WARNING: html truncated to prevent buffer overflow"); + return 0; + } + } + else + { + snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + "

    "\ + "

    Firmware Update

    "\ + "

    "\ + ""\ + ); + if(sel == 2) + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + "

    Firmware upload interrupted. Please try again.

    "\ + ); + } + else if(sel == 3) + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + "

    Firmware upload failed. Unable to verify firmware. Please try again, or check the integrity of the firmware.

    "\ + ); + } + else if(sel == 4) + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + "

    Firmware upload in progress. Please try again in 30 seconds.

    "\ + ); + } + else + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + "

    Firmware upload failed. Please try again.

    "\ + ); + } + + if( snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + ""\ + ""\ + ) < SHARED_BUFFER_LEN) + { + TRACE("http.c: html written to buffer"); + return 1; + } + else + { + TRACE("http.c: WARNING: html truncated to prevent buffer overflow"); + return 0; + } + } +} + /* * Create and format HTML for display help page * */ static uint8_t interfaceCreate_Display_Home(void) { - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ""\ ""\ ""\ ""\ @@ -1811,7 +2467,11 @@ static uint8_t interfaceCreate_Display_Home(void) "

    "\ "

    Flows

    "\ "

    "\ - "View the current flows in the flow table. This page is currently limited to displaying a maximum of 5 flows."\ + "View the current flows in the flow table. 4 flows are displayed per page."\ + "

    "\ + "

    Meters

    "\ + "

    "\ + "View the current meters in the meter table. 3 meters are displayed per page. Up to 8 meters can be configured, with up to 3 meter bands each."\ "

    "\ ""\ ""\ @@ -1863,20 +2523,18 @@ static uint8_t interfaceCreate_Display_Ports(uint8_t step) vlCtr++; } } + + sprintf(shared_buffer, http_header); - snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ + ""\ ""\ ""\ ""\ ""\ ""\ @@ -1905,7 +2566,7 @@ static uint8_t interfaceCreate_Display_Ports(uint8_t step) "Ports"\ ""\ ""\ - ""\ + ""\ ""\ ""\ ""\ @@ -2085,8 +2746,8 @@ static uint8_t interfaceCreate_Display_Ports(uint8_t step) ""\ "
    Port 1Port 2Port 3
    "\ "
    "\ - ""\ - "
    "\ + ""\ + "
    "\ ""\ ""\ ""\ @@ -2164,8 +2825,8 @@ static uint8_t interfaceCreate_Display_Ports(uint8_t step) ""\ ""\ "
    "\ - ""\ - "
    "\ + ""\ + "
    "\ ""\ ""\ ""\ @@ -2206,11 +2867,7 @@ static uint8_t interfaceCreate_Display_OpenFlow(void) // Status char wi_ofStatus[15] = ""; - if (tcp_pcb->state != ESTABLISHED && Zodiac_Config.OFEnabled == OF_ENABLED) - { - snprintf(wi_ofStatus, 15, "Disconnected"); - } - else if (tcp_pcb->state == ESTABLISHED && Zodiac_Config.OFEnabled == OF_ENABLED) + if (tcp_con_state == 1 && tcp_pcb->state == ESTABLISHED && Zodiac_Config.OFEnabled == OF_ENABLED) { snprintf(wi_ofStatus, 15, "Connected"); } @@ -2220,7 +2877,7 @@ static uint8_t interfaceCreate_Display_OpenFlow(void) } else { - snprintf(wi_ofStatus, 15, "Error: unknown"); + snprintf(wi_ofStatus, 15, "Disconnected"); } // Version, Tables, Flows, Lookups, Matches @@ -2267,25 +2924,25 @@ static uint8_t interfaceCreate_Display_OpenFlow(void) snprintf(wi_ofVersion, 15, "Auto"); } - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ + ""\ ""\ ""\ ""\ ""\ ""\ "

    "\ "

    OpenFlow Information

    "\ "

    "\ - "
    "\ + ""\ "
    "\ "OpenFlow"\ "Status:
    "\ @@ -2323,41 +2980,36 @@ static uint8_t interfaceCreate_Display_OpenFlow(void) */ static uint8_t interfaceCreate_Display_Flows(void) { - snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ""\ ""\ ""\ ""\ "

    "\ "

    Flows

    "\ "

    "\ - "
    "\
    +				"
    "\
     			);
     
     // Begin Flow formatting
     
     int i;
    -uint8_t flowLimit;
    +uint8_t flowEnd = flowBase + FLOW_DISPLAY_LIMIT;
     struct ofp_action_header * act_hdr;
     
    -// Limit flows to fit in shared_buffer
    -if(iLastFlow < 5)
    -{
    -	flowLimit = iLastFlow;
    -}
    -else
    +// Ensure page correctly displays end of flows
    +if(iLastFlow < flowEnd)
     {
    -	flowLimit = 5;
    +	flowEnd = iLastFlow;
     }
     
     if (iLastFlow > 0)
    @@ -2365,11 +3017,11 @@ if (iLastFlow > 0)
     	// OpenFlow v1.0 (0x01) Flow Table
     	if( OF_Version == 1)
     	{
    -		snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\r\n-------\r\n");
    -		for (i=0;imatch.in_port), ntohs(flow_match10[i]->match.dl_type));
     			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"  Source MAC: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\t\tDestination MAC: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\r\n",flow_match10[i]->match.dl_src[0], flow_match10[i]->match.dl_src[1], flow_match10[i]->match.dl_src[2], flow_match10[i]->match.dl_src[3], flow_match10[i]->match.dl_src[4], flow_match10[i]->match.dl_src[5] \
     			, flow_match10[i]->match.dl_dst[0], flow_match10[i]->match.dl_dst[1], flow_match10[i]->match.dl_dst[2], flow_match10[i]->match.dl_dst[3], flow_match10[i]->match.dl_dst[4], flow_match10[i]->match.dl_dst[5]);
    @@ -2441,7 +3093,7 @@ if (iLastFlow > 0)
     				}
     			}
     		}
    -		snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\r\n-------\r\n\n");
    +		snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"_______\r\n\n");
     	}
     	// OpenFlow v1.3 (0x04) Flow Table
     	if( OF_Version == 4)
    @@ -2459,11 +3111,11 @@ if (iLastFlow > 0)
     		uint8_t oxm_ipv4[4];
     		uint16_t oxm_ipv6[8];
     
    -		snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\r\n-------\r\n");
    -		for (i=0;imatch.length)-4))
    @@ -2574,22 +3226,38 @@ if (iLastFlow > 0)
     			int min = t/60;
     			int sec = t%60;
     			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"  Last Match: %02d:%02d:%02d\r\n", hr, min, sec);
    +						
     			// Print instruction list
     			if (ofp13_oxm_inst[i] != NULL)
     			{
    +				// Get a list of all instructions for this flow
    +				void *insts[8] = {0};
    +				inst_size = 0;
    +				while(inst_size < ofp13_oxm_inst_size[i]){
    +					struct ofp13_instruction *inst_ptr = (struct ofp13_instruction *)(ofp13_oxm_inst[i] + inst_size);
    +					insts[ntohs(inst_ptr->type)] = inst_ptr;
    +					inst_size += ntohs(inst_ptr->len);
    +				}
    +						
     				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\r Instructions:\r\n");
    -				inst_ptr = (struct ofp13_instruction *) ofp13_oxm_inst[i];
    -				inst_size = ntohs(inst_ptr->len);
    -				if(ntohs(inst_ptr->type) == OFPIT13_APPLY_ACTIONS)
    +						
    +				// Check for optional metering instruction
    +				if(insts[OFPIT13_METER] != NULL)						
    +				{
    +					struct ofp13_instruction_meter *inst_meter = insts[OFPIT13_METER];
    +					snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"  Meter: %d\r\n", ntohl(inst_meter->meter_id));
    +				}
    +						
    +				if(insts[OFPIT13_APPLY_ACTIONS] != NULL)
     				{
     					snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"  Apply Actions:\r\n");
     					struct ofp13_action_header *act_hdr;
     					act_size = 0;
    -					if (inst_size == sizeof(struct ofp13_instruction_actions)) snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"   DROP \r\n");	// No actions
    -					while (act_size < (inst_size - sizeof(struct ofp13_instruction_actions)))
    +					inst_actions = insts[OFPIT13_APPLY_ACTIONS];
    +					if (ntohs(inst_actions->len) == sizeof(struct ofp13_instruction_actions)) snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"   DROP \r\n");	// No actions
    +					while (act_size < (ntohs(inst_actions->len) - sizeof(struct ofp13_instruction_actions)))
     					{
    -						inst_actions  = ofp13_oxm_inst[i] + act_size;
    -						act_hdr = &inst_actions->actions;
    +						act_hdr = (struct ofp13_action_header*)((uintptr_t)inst_actions->actions + act_size);
     						if (htons(act_hdr->type) == OFPAT13_OUTPUT)
     						{
     							struct ofp13_action_output *act_output = act_hdr;
    @@ -2732,26 +3400,11 @@ if (iLastFlow > 0)
     					}
     				}
     				// Print goto table instruction
    -				if(ntohs(inst_ptr->type) == OFPIT13_GOTO_TABLE)
    +				if(insts[OFPIT13_GOTO_TABLE] != NULL)
     				{
     					struct ofp13_instruction_goto_table *inst_goto_ptr;
    -					inst_goto_ptr = (struct ofp13_instruction_goto_table *) inst_ptr;
    +					inst_goto_ptr = (struct ofp13_instruction_goto_table *) insts[OFPIT13_GOTO_TABLE];
     					snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"  Goto Table: %d\r\n", inst_goto_ptr->table_id);
    -					continue;
    -				}
    -				// Is there more then one instruction?
    -				if (ofp13_oxm_inst_size[i] > inst_size)
    -				{
    -					uint8_t *nxt_inst;
    -					nxt_inst = ofp13_oxm_inst[i] + inst_size;
    -					inst_ptr = (struct ofp13_instruction *) nxt_inst;
    -					inst_size = ntohs(inst_ptr->len);
    -					if(ntohs(inst_ptr->type) == OFPIT13_GOTO_TABLE)
    -					{
    -						struct ofp13_instruction_goto_table *inst_goto_ptr;
    -						inst_goto_ptr = (struct ofp13_instruction_goto_table *) inst_ptr;
    -						snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"  Goto Table: %d\r\n", inst_goto_ptr->table_id);
    -					}
     				}
     				} else {
     				// No instructions
    @@ -2759,25 +3412,49 @@ if (iLastFlow > 0)
     				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"   DROP \r\n");
     			}
     		}
    -		snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\r\n-------\r\n\n");
    +		snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"_______\r\n\n");
     	}
     	} else {
    -	snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"No Flows installed!\r\n");
    +	snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"No Flows installed\r\n");
     	}
     	
     // End Flow formatting
     
    -	if( snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\
    -				"
    "\ - /*""\ - "
    "\ - ""\ - "
    "\ - ""\ - "
    "\*/ + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + "
    "\ + "

    "\ + ); + + // Check if "previous page" button needs to be created + if(flowBase >= FLOW_DISPLAY_LIMIT) + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + ""\ + ); + } + + // Check if "next page" button needs to be created + if(flowEnd < iLastFlow) + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + ""\ + ); + } + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + "
    "); + + // Check if "clear flows" button needs to be created + if(iLastFlow > 0) + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ "
    "\ - "
    "\ + "
    "\ "
    "\ + ); + } + + if( snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ) < SHARED_BUFFER_LEN) @@ -2791,25 +3468,207 @@ if (iLastFlow > 0) return 0; } } + +/* +* Create and format HTML for display meters page +* +*/ +static uint8_t interfaceCreate_Display_Meters(void) +{ + /* Prepare meter counters */ + + // Check status of start of range + if(meterBase >= iLastMeter) + { + meterBase = 0; + } + + // Find number of meters + int meterCount; + if(meter_entry[0] == NULL) + { + meterCount = 0; + } + else + { + meterCount = iLastMeter; + } + + // Find end of display range (exclusive) - meterBase indexes the start of the range + int meterEnd; + if(meterBase + METER_DISPLAY_LIMIT >= iLastMeter) + { + meterEnd = iLastMeter; + } + else + { + meterEnd = meterBase + METER_DISPLAY_LIMIT; + } + + // Format header + sprintf(shared_buffer, http_header); + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + "

    "\ + "

    Meters

    "\ + "%d meters configured
    "\ + , meterCount); + if(meterCount != 0) + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + "Showing meters %d - %d
    "\ + , meterBase+1, meterEnd); + } + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + "

    "\ + "
    "\
    +			);
    +
    +// Begin Meter formatting
    +		
    +	// Check that table is populated
    +	if(meter_entry[0] != NULL)
    +	{
    +		int meter_index = meterBase;
    +		while(meter_entry[meter_index] != NULL && meter_index < meterEnd)
    +		{
    +			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"_______\r\n");
    +			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\r\nMeter %d\r\n", meter_index+1);
    +			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"  Meter ID: %d\r\n", meter_entry[meter_index]->meter_id);
    +			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"  Counters:\r\n");
    +			meter_entry[meter_index]->flow_count = get_bound_flows(meter_entry[meter_index]->meter_id);
    +			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\tBound Flows:\t%d\tDuration:\t%d sec\r\n", meter_entry[meter_index]->flow_count, (sys_get_ms()-meter_entry[meter_index]->time_added)/1000);
    +			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\tByte Count:\t%"PRIu64"\tPacket Count:\t%"PRIu64"\r\n", meter_entry[meter_index]->byte_in_count, meter_entry[meter_index]->packet_in_count);
    +			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\tConfiguration:\t");
    +			if(((meter_entry[meter_index]->flags) & OFPMF13_KBPS) == OFPMF13_KBPS)
    +			{
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"KBPS; ");
    +			}
    +			if(((meter_entry[meter_index]->flags) & OFPMF13_PKTPS) == OFPMF13_PKTPS)
    +			{
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"PKTPS; ");
    +			}
    +			if(((meter_entry[meter_index]->flags) & OFPMF13_BURST) == OFPMF13_BURST)
    +			{
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"BURST; ");
    +			}
    +			if(((meter_entry[meter_index]->flags) & OFPMF13_STATS) == OFPMF13_STATS)
    +			{
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"STATS; ");
    +			}
    +			if(meter_entry[meter_index]->flags == 0)
    +			{
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer)," NONE;");
    +			}
    +				
    +			snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\r\n\tNumber of bands:\t%d\r\n", meter_entry[meter_index]->band_count);
    +			int bands_processed = 0;
    +			struct ofp13_meter_band_drop * ptr_band;
    +			ptr_band = &(meter_entry[meter_index]->bands);
    +			while(bands_processed < meter_entry[meter_index]->band_count)
    +			{
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\t\tBand %d:\r\n", bands_processed+1);
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\t\t  Type:\t\t");
    +				if(ptr_band->type == OFPMBT13_DROP)
    +				{
    +					snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"DROP\r\n");
    +				}
    +				else if(ptr_band->type == OFPMBT13_DSCP_REMARK)
    +				{
    +					snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"DSCP REMARK (unsupported)\r\n");
    +				}
    +				else
    +				{
    +					snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"unsupported type\r\n");
    +				}
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\t\t  Rate:\t\t%d\t\r\n", ptr_band->rate);
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\t\t  Burst Size:\t%d\t\r\n", ptr_band->burst_size);
    +					
    +				// Find band index
    +				int band_index = ((uint8_t*)ptr_band - (uint8_t*)&(meter_entry[meter_index]->bands)) / sizeof(struct ofp13_meter_band_drop);
    +					
    +				// Display counters
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\t\t  Byte count:\t%"PRIu64"\t\r\n", band_stats_array[meter_index].band_stats[band_index].byte_band_count);
    +				snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\t\t  Packet count:\t%"PRIu64"\t\r\n", band_stats_array[meter_index].band_stats[band_index].packet_band_count);
    +					
    +				ptr_band++;	// Move to next band
    +				bands_processed++;
    +			}
    +			meter_index++;
    +		}
    +		snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),"\r\n_______\r\n\r\n");
    +	}
    +	
    +// End Meter formatting
    +
    +	snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\
    +				"
    "\ + "
    "\ + ); + + // Check if "previous page" button needs to be created + if(meterBase >= METER_DISPLAY_LIMIT) + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + ""\ + ); + } + + // Check if "next page" button needs to be created + if(meterEnd < iLastMeter) + { + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + ""\ + ); + } + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + "
    "); + + if( snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + ""\ + ""\ + ) < SHARED_BUFFER_LEN) + { + TRACE("http.c: html written to buffer"); + return 1; + } + else + { + TRACE("http.c: WARNING: html truncated to prevent buffer overflow"); + return 0; + } + +} + /* * Create and format HTML for config help page * */ static uint8_t interfaceCreate_Config_Home(void) { - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ""\ ""\ ""\ ""\ @@ -2847,42 +3706,40 @@ static uint8_t interfaceCreate_Config_Home(void) */ static uint8_t interfaceCreate_Config_Network(void) { - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ - ""\ - ""\ - ""\ - ""\ - ""\ - "

    "\ - "

    Network Configuration

    "\ - "

    "\ - "
    "\ - "
    "\ - "Connection"\ - "Name:
    "\ - "

    "\ - "MAC Address:
    "\ - "

    "\ - "IP Address:
    "\ - "

    "\ - "Netmask:
    "\ - "

    "\ - "Gateway:
    "\ - "

    "\ - ""\ - ""\ - "
    "\ - "
    "\ - ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + "

    "\ + "

    Network Configuration

    "\ + "

    "\ + "
    "\ + "
    "\ + "Connection"\ + "Name:
    "\ + "

    "\ + "MAC Address:
    "\ + "

    "\ + "IP Address:
    "\ + "

    "\ + "Netmask:
    "\ + "

    "\ + "Gateway:
    "\ + "

    "\ + ""\ + ""\ + "
    "\ + "
    "\ + ""\ ""\ , Zodiac_Config.device_name\ , Zodiac_Config.MAC_address[0], Zodiac_Config.MAC_address[1], Zodiac_Config.MAC_address[2], Zodiac_Config.MAC_address[3], Zodiac_Config.MAC_address[4], Zodiac_Config.MAC_address[5]\ @@ -2912,18 +3769,16 @@ static uint8_t interfaceCreate_Config_VLANs(void) char wi_vlType[10] = ""; // Opening tags, and base table - snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ""\ ""\ ""\ ""\ "

    "\ "

    OpenFlow Configuration

    "\ "

    "\ - "
    "\ + ""\ "
    "\ "OpenFlow"\ ); @@ -3162,18 +4015,15 @@ static uint8_t interfaceCreate_Config_OpenFlow(void) */ static uint8_t interfaceCreate_About(void) { - if( snprintf(shared_buffer, SHARED_BUFFER_LEN,\ + sprintf(shared_buffer, http_header); + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ ""\ ""\ ""\ ""\ ""\ ""\ @@ -3201,3 +4051,39 @@ static uint8_t interfaceCreate_About(void) return 0; } } + +/* +* Create and format HTML for interstitial restart page +* +*/ +static uint8_t interfaceCreate_Restart(void) +{ + sprintf(shared_buffer, http_header); + snprintf(shared_buffer+strlen(shared_buffer), SHARED_BUFFER_LEN-strlen(shared_buffer),\ + ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + "

    "\ + "Restarting..."\ + "

    "\ + ""\ + ""\ + ) < SHARED_BUFFER_LEN) + { + TRACE("http.c: html written to buffer"); + return 1; + } + else + { + TRACE("http.c: WARNING: html truncated to prevent buffer overflow"); + return 0; + } +} \ No newline at end of file diff --git a/ZodiacFX/src/http.h b/ZodiacFX/src/http.h index a2eb700..fac8f53 100644 --- a/ZodiacFX/src/http.h +++ b/ZodiacFX/src/http.h @@ -31,6 +31,19 @@ #ifndef HTTP_H_ #define HTTP_H_ +#define FLOW_DISPLAY_LIMIT 4 // Displayable flows per page +#define METER_DISPLAY_LIMIT 3 // Displayable meters per page +#define BOUNDARY_MAX_LEN 70 +#define UPLOAD_TIMEOUT 25000 // (ms) timeout window between each firmware update packet +#define MAX_CONN 4 // Maximum http connection status structs + +struct http_conns +{ + int bytes_waiting; + struct tcp_pcb *attached_pcb; + uint32_t timeout; +}; + void http_init(void); #endif /* HTTP_H_ */ \ No newline at end of file diff --git a/ZodiacFX/src/openflow/of_helper.c b/ZodiacFX/src/openflow/of_helper.c index 48cfe54..db2f6b2 100644 --- a/ZodiacFX/src/openflow/of_helper.c +++ b/ZodiacFX/src/openflow/of_helper.c @@ -42,18 +42,22 @@ #include "lwip/tcp_impl.h" #include "lwip/udp.h" #include "switch.h" +#include "timers.h" #define ALIGN8(x) (x+7)/8*8 // Global variables extern struct zodiac_config Zodiac_Config; extern int iLastFlow; +extern int iLastMeter; extern int OF_Version; extern int totaltime; extern uint8_t last_port_status[4]; extern uint8_t port_status[4]; extern struct flows_counter flow_counters[MAX_FLOWS_13]; extern struct table_counter table_counters[MAX_TABLES]; +extern struct meter_entry13 *meter_entry[MAX_METER_13]; +extern struct meter_band_stats_array band_stats_array[MAX_METER_13]; extern struct ofp_flow_mod *flow_match10[MAX_FLOWS_10]; extern struct flow_tbl_actions *flow_actions10[MAX_FLOWS_10]; extern struct ofp13_flow_mod *flow_match13[MAX_FLOWS_13]; @@ -1094,6 +1098,7 @@ void flow_timeouts() void clear_flows(void) { iLastFlow = 0; + iLastMeter = 0; membag_init(); /* Clear OpenFlow 1.0 flow table */ @@ -1126,6 +1131,15 @@ void clear_flows(void) table_counters[x].lookup_count = 0; table_counters[x].matched_count = 0; } + + /* Clear Meter Table Pointers*/ + for(int x=0; xmeter_id == id) + { + TRACE("of_helper.c: meter entry found - continuing"); + break; + } + + meter_index++; + } + if(meter_entry[meter_index] == NULL || meter_index == MAX_METER_13) + { + TRACE("of_helper.c: meter entry not found - packet not dropped"); + return SUCCESS; + } + // meter_index now holds the meter bound to the current flow + + // Update meter counters + meter_entry[meter_index]->byte_in_count += bytes; + (meter_entry[meter_index]->packet_in_count)++; + + // Check if meter has been used before + if(meter_entry[meter_index]->last_packet_in == 0) + { + // Update timer + meter_entry[meter_index]->last_packet_in = sys_get_ms(); + + TRACE("of_helper.c: first hit of meter - packet not dropped"); + return SUCCESS; + } + + // Find time delta + uint64_t time_delta = sys_get_ms() - meter_entry[meter_index]->last_packet_in; + + // Update timer + meter_entry[meter_index]->last_packet_in = sys_get_ms(); + + // Check configuration flags + uint32_t calculated_rate = 0; + if(((meter_entry[meter_index]->flags) & OFPMF13_KBPS) == OFPMF13_KBPS) + { + calculated_rate = ((bytes*8)/time_delta); // bit/ms == kbit/s + TRACE("of_helper.c: calculated rate - %d kbps", calculated_rate); + } + else if(((meter_entry[meter_index]->flags) & OFPMF13_PKTPS) == OFPMF13_PKTPS) + { + calculated_rate = 1000/time_delta; + TRACE("of_helper.c: calculated rate - %d pktps", calculated_rate); + } + else + { + TRACE("of_helper.c: unsupported meter configuration - packet not dropped"); + return SUCCESS; + } + + // Check each band + int bands_processed = 0; + uint32_t highest_rate = 0; // Highest triggered band rate + struct ofp13_meter_band_drop * ptr_highest_band = NULL; // Store pointer to highest triggered band + struct ofp13_meter_band_drop * ptr_band; + ptr_band = &(meter_entry[meter_index]->bands); + while(bands_processed < meter_entry[meter_index]->band_count) + { + if(calculated_rate >= ptr_band->rate) + { + if(ptr_band->rate > highest_rate) + { + highest_rate = ptr_band->rate; // Update highest triggered band rate + ptr_highest_band = ptr_band; // Update highest triggered band + } + } + + ptr_band++; // Move to next band + bands_processed++; + } + + // Check if any bands triggered + if(highest_rate == 0 || ptr_highest_band == NULL) + { + TRACE("of_helper.c: no bands triggered - packet not dropped"); + return SUCCESS; + } + + // Check band type + if(ptr_highest_band->type != OFPMBT13_DROP) + { + TRACE("of_helper.c: unsupported band type - not dropping packet"); + return SUCCESS; + } + + TRACE("of_helper.c: highest triggered band rate:%d", highest_rate); + + /* Update band counters */ + // Find band index + int band_index = ((uint8_t*)ptr_highest_band - (uint8_t*)&(meter_entry[meter_index]->bands)) / sizeof(struct ofp13_meter_band_drop); + + // Update counters + band_stats_array[meter_index].band_stats[band_index].byte_band_count += bytes; + band_stats_array[meter_index].band_stats[band_index].packet_band_count++; + + TRACE("of_helper.c: packet needs to be dropped"); + return FAILURE; +} + +/* +* Retrieve number of flows bound to the specified meter +* +* @param id - meter ID to check +* +* @ret count - number of associated flows +* +*/ +uint32_t get_bound_flows(uint32_t id) +{ + uint32_t count = 0; + + // Loop through flows + for (int i=0;itype)] = inst_ptr; + inst_size += ntohs(inst_ptr->len); + } + + // Check if metering instruction is present + if(insts[OFPIT13_METER] != NULL) + { + struct ofp13_instruction_meter *inst_meter = insts[OFPIT13_METER]; + // Check the found meter id + if(ntohl(inst_meter->meter_id) == id) + { + // The flow's instruction matches the specified meter id + count++; // increment the counter + } + } + } + + return count; +} diff --git a/ZodiacFX/src/openflow/of_helper.h b/ZodiacFX/src/openflow/of_helper.h index ff02700..0757a71 100644 --- a/ZodiacFX/src/openflow/of_helper.h +++ b/ZodiacFX/src/openflow/of_helper.h @@ -61,5 +61,7 @@ int flow_stats_msg13(char *buffer, int first, int last); void set_ip_checksum(uint8_t *p_uc_data, int packet_size, int iphdr_offset); void remove_flow13(int flow_id); void remove_flow10(int flow_id); +int meter_handler(uint32_t id, uint16_t bytes); +uint32_t get_bound_flows(uint32_t id); #endif /* OF_HELPER_H_ */ diff --git a/ZodiacFX/src/openflow/openflow.c b/ZodiacFX/src/openflow/openflow.c index e2beac1..f34975b 100644 --- a/ZodiacFX/src/openflow/openflow.c +++ b/ZodiacFX/src/openflow/openflow.c @@ -57,6 +57,9 @@ struct flows_counter flow_counters[MAX_FLOWS_13]; struct flow_tbl_actions *flow_actions10[MAX_FLOWS_10]; struct table_counter table_counters[MAX_TABLES]; int iLastFlow = 0; +int iLastMeter = 0; +struct meter_entry13 *meter_entry[MAX_METER_13]; +struct meter_band_stats_array band_stats_array[MAX_METER_13]; uint8_t shared_buffer[SHARED_BUFFER_LEN]; char sysbuf[64]; struct ip_addr serverIP; @@ -69,6 +72,8 @@ int tcp_wait = 0; int totaltime = 0; int heartbeat = 0; int multi_pos; +uint32_t reply_more_xid = 0; +bool reply_more_flag = false; bool rcv_freq; // Internal Functions @@ -78,6 +83,7 @@ void echo_reply(uint32_t xid); err_t TCPready(void *arg, struct tcp_pcb *tpcb, err_t err); void tcp_error(void * arg, err_t err); static err_t of_receive(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); +static err_t of_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); /* * Converts a 64bit value from host to network format @@ -202,6 +208,24 @@ static err_t of_receive(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t e return ERR_OK; } +/* +* OpenFlow Sent callback function +* +* @param *arg - pointer the additional TCP args +* @param *tcp_pcb - pointer the TCP session structure. +* +*/ +static err_t of_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len) +{ + TRACE("openflow.c: [of_sent] %d bytes acknowledged ", len); + if(reply_more_flag == true) + { + multi_flow_more_reply13(); + } + + return ERR_OK; +} + /* * OpenFlow HELLO message function * @@ -377,6 +401,7 @@ err_t TCPready(void *arg, struct tcp_pcb *tpcb, err_t err) tcp_recv(tpcb, of_receive); tcp_poll(tpcb, NULL, 4); tcp_err(tpcb, NULL); + tcp_sent(tpcb, of_sent); if(Zodiac_Config.failstate == 0) clear_flows(); // Clear the flow if in secure mode TRACE("openflow.c: Connected to controller"); OF_hello(); diff --git a/ZodiacFX/src/openflow/openflow.h b/ZodiacFX/src/openflow/openflow.h index 00a63d7..ad8e60f 100644 --- a/ZodiacFX/src/openflow/openflow.h +++ b/ZodiacFX/src/openflow/openflow.h @@ -33,6 +33,7 @@ #include "openflow_spec/openflow_spec10.h" #include "openflow_spec/openflow_spec13.h" #include "of_helper.h" +#include "config_zodiac.h" #include struct flows_counter @@ -66,12 +67,46 @@ struct oxm_header13 uint8_t oxm_len; }; +/* +* OpenFlow meter entry structure +* Meter table is populated with these entries. +* The structure contains: +* - meter ID +* - counters +* - meter bands +*/ +struct meter_entry13 +{ + uint32_t meter_id; + uint32_t flow_count; // Number of flows bound to meter + uint64_t packet_in_count; // Packets processed by meter + uint64_t byte_in_count; // Bytes processed by meter + uint32_t time_added; // Time meter was added in ms (time alive calculated when required) + uint16_t flags; // Meter configuration flags + uint16_t band_count; // Number of bands in this meter + uint64_t last_packet_in; // Time when meter last processed a packet (milliseconds) + struct ofp13_meter_band_drop bands[0]; // Meter bands +}; + +/* +* Meter band counters +* Each instance of meter_band_stats_array contains +* statistics for the maximum number of supported +* bands. +* +*/ +struct meter_band_stats_array +{ + struct ofp13_meter_band_stats band_stats[MAX_METER_BANDS_13]; +}; + void task_openflow(void); void nnOF_tablelookup(uint8_t *p_uc_data, uint32_t *ul_size, int port); void nnOF10_tablelookup(uint8_t *p_uc_data, uint32_t *ul_size, int port); void nnOF13_tablelookup(uint8_t *p_uc_data, uint32_t *ul_size, int port); void of10_message(struct ofp_header *ofph, int size, int len); void of13_message(struct ofp_header *ofph, int size, int len); +void multi_flow_more_reply13(void); void barrier10_reply(uint32_t xid); void barrier13_reply(uint32_t xid); void sendtcp(const void *buffer, u16_t len); @@ -90,4 +125,9 @@ void port_status_message13(uint8_t port); #define SHARED_BUFFER_LEN 2048 +#define METER_PARTIAL 8 // Meter structure length, excluding header and bands + +#define SUCCESS 0 +#define FAILURE 1 + #endif /* OPENFLOW_H_ */ diff --git a/ZodiacFX/src/openflow/openflow_13.c b/ZodiacFX/src/openflow/openflow_13.c index d8d33a7..7de7391 100644 --- a/ZodiacFX/src/openflow/openflow_13.c +++ b/ZodiacFX/src/openflow/openflow_13.c @@ -38,6 +38,7 @@ #include "lwip/tcp.h" #include "ipv4/lwip/ip.h" #include "lwip/inet_chksum.h" +#include "timers.h" #define ALIGN8(x) (x+7)/8*8 @@ -48,8 +49,11 @@ extern struct tcp_pcb *tcp_pcb; extern int OF_Version; extern bool rcv_freq; extern int iLastFlow; +extern int iLastMeter; extern int totaltime; extern struct ofp13_flow_mod *flow_match13[MAX_FLOWS_13]; +extern struct meter_entry13 *meter_entry[MAX_METER_13]; +extern struct meter_band_stats_array band_stats_array[MAX_METER_13]; extern uint8_t *ofp13_oxm_match[MAX_FLOWS_13]; extern uint8_t *ofp13_oxm_inst[MAX_FLOWS_13]; extern uint16_t ofp13_oxm_inst_size[MAX_FLOWS_13]; @@ -61,6 +65,9 @@ extern struct ofp_switch_config Switch_config; extern uint8_t shared_buffer[SHARED_BUFFER_LEN]; extern int multi_pos; extern uint8_t NativePortMatrix; +extern bool reply_more_flag; +extern uint32_t reply_more_xid; +extern int meter_handler(uint32_t id, uint16_t bytes); // Internal functions void features_reply13(uint32_t xid); @@ -79,8 +86,15 @@ int multi_portdesc_reply13(uint8_t *buffer, struct ofp13_multipart_request * req int multi_table_reply13(uint8_t *buffer, struct ofp13_multipart_request *req); int multi_tablefeat_reply13(uint8_t *buffer, struct ofp13_multipart_request *msg); int multi_flow_reply13(uint8_t *buffer, struct ofp13_multipart_request *msg); +int multi_meter_stats_reply13(uint8_t *buffer, struct ofp13_multipart_request * req); +int multi_meter_config_reply13(uint8_t *buffer, struct ofp13_multipart_request * req); +int multi_meter_features_reply13(uint8_t *buffer, struct ofp13_multipart_request * req); void packet_in13(uint8_t *buffer, uint16_t ul_size, uint8_t port, uint8_t reason, int flow); void packet_out13(struct ofp_header *msg); +void meter_mod13(struct ofp_header *msg); +void meter_add13(struct ofp_header *msg); +void meter_modify13(struct ofp_header *msg); +void meter_delete13(struct ofp_header *msg); /* * Converts a 64bit value from host to network format @@ -109,6 +123,7 @@ void nnOF13_tablelookup(uint8_t *p_uc_data, uint32_t *ul_size, int port) return; } TRACE("openflow_13.c: Matched flow %d, table %d", i+1, table_id); + flow_counters[i].hitCount++; // Increment flow hit count flow_counters[i].bytes += packet_size; flow_counters[i].lastmatch = (totaltime/2); // Increment flow hit count @@ -127,13 +142,23 @@ void nnOF13_tablelookup(uint8_t *p_uc_data, uint32_t *ul_size, int port) insts[ntohs(inst_ptr->type)] = inst_ptr; inst_size += ntohs(inst_ptr->len); } + + if(insts[OFPIT13_METER] != NULL) + { + struct ofp13_instruction_meter *inst_meter = insts[OFPIT13_METER]; + if(meter_handler(ntohl(inst_meter->meter_id), packet_size) == FAILURE) // Process meter id (provide byte count for counters) + { + // Packet must be dropped + return; + } + } if(insts[OFPIT13_APPLY_ACTIONS] != NULL) { bool recalculate_ip_checksum = false; struct ofp13_instruction_actions *inst_actions = insts[OFPIT13_APPLY_ACTIONS]; int act_size = 0; - while (act_size < (inst_size - sizeof(struct ofp13_instruction_actions))) + while (act_size < (ntohs(inst_actions->len) - sizeof(struct ofp13_instruction_actions))) { struct ofp13_action_header *act_hdr = (struct ofp13_action_header*)((uintptr_t)inst_actions->actions + act_size); switch (htons(act_hdr->type)) @@ -534,6 +559,20 @@ void of13_message(struct ofp_header *ofph, int size, int len) multi_pos += multi_portdesc_reply13(&shared_buffer[multi_pos], multi_req); } + if ( ntohs(multi_req->type) == OFPMP13_METER ) + { + multi_pos += multi_meter_stats_reply13(&shared_buffer[multi_pos], multi_req); + } + + if ( ntohs(multi_req->type) == OFPMP13_METER_CONFIG ) + { + multi_pos += multi_meter_config_reply13(&shared_buffer[multi_pos], multi_req); + } + + if ( ntohs(multi_req->type) == OFPMP13_METER_FEATURES ) + { + multi_pos += multi_meter_features_reply13(&shared_buffer[multi_pos], multi_req); + } if ( htons(multi_req->type) == OFPMP13_TABLE_FEATURES ) { @@ -556,6 +595,10 @@ void of13_message(struct ofp_header *ofph, int size, int len) case OFPT13_BARRIER_REQUEST: barrier13_reply(ofph->xid); break; + + case OFPT13_METER_MOD: + meter_mod13(ofph); + break; }; if (size == len && multi_pos !=0) @@ -684,6 +727,7 @@ int multi_desc_reply13(uint8_t *buffer, struct ofp13_multipart_request *msg) */ int multi_flow_reply13(uint8_t *buffer, struct ofp13_multipart_request *msg) { + int len = 0; char statsbuffer[2048]; struct ofp13_multipart_reply *reply; reply = (struct ofp13_multipart_reply *) buffer; @@ -692,13 +736,76 @@ int multi_flow_reply13(uint8_t *buffer, struct ofp13_multipart_request *msg) reply->header.xid = msg->header.xid; reply->flags = 0; reply->type = htons(OFPMP13_FLOW); - int len = flow_stats_msg13(&statsbuffer, 0, iLastFlow); + if(iLastFlow > 15) + { + // Only send first 15 flows + len = flow_stats_msg13(&statsbuffer, 0, 15); + reply->flags = htons(OFPMPF13_REPLY_MORE); // More replies will follow + reply_more_flag = true; // Notify of_sent that more messages need to be sent + reply_more_xid = msg->header.xid; // Store xid for later replies + } + else + { + // Send all flows + len = flow_stats_msg13(&statsbuffer, 0, iLastFlow); + } memcpy(reply->body, &statsbuffer, len); len += sizeof(struct ofp13_multipart_reply); reply->header.length = htons(len); return len; } +/* +* OpenFlow reply more stats function +* +* @param xid - transaction ID +* +*/ +void multi_flow_more_reply13(void) +{ + // Clear shared_buffer + memset(&shared_buffer, 0, SHARED_BUFFER_LEN); + + static int startFlow = 15; + int len = 0; + char statsbuffer[2048]; + struct ofp13_multipart_reply *reply; + reply = (struct ofp13_multipart_reply *) shared_buffer; + reply->header.version = OF_Version; + reply->header.type = OFPT13_MULTIPART_REPLY; + reply->header.xid = reply_more_xid; + reply->type = htons(OFPMP13_FLOW); + if((startFlow+15) < iLastFlow) + { + // Send first 15 flows + TRACE("openflow_13.c: writing flow stats: %d to %d", startFlow, (startFlow+15)); + len = flow_stats_msg13(&statsbuffer, startFlow, (startFlow+15)); + reply->flags = htons(OFPMPF13_REPLY_MORE); // More replies will follow + reply_more_flag = true; // Notify of_sent that more messages need to be sent + startFlow += 15; + } + else + { + // Finish sending flows + TRACE("openflow_13.c: writing final flow stats: %d to %d", startFlow, iLastFlow); + len = flow_stats_msg13(&statsbuffer, startFlow, iLastFlow); + reply->flags = 0; // No more replies will follow + reply_more_flag = false; // Notify of_sent that no more messages need to be sent + reply_more_xid = 0; // Clear stored xid + startFlow = 15; // Reset startFlow + } + memcpy(reply->body, &statsbuffer, len); + len += sizeof(struct ofp13_multipart_reply); + reply->header.length = htons(len); + + if (len < 2048) + { + TRACE("openflow_13.c: sending flow stats"); + sendtcp(&shared_buffer, len); + } + return; +} + /* * OpenFlow Multi-part AGGREGATE reply message function * @@ -1021,6 +1128,378 @@ int multi_portstats_reply13(uint8_t *buffer, struct ofp13_multipart_request *msg return len; } +/* +* Main OpenFlow Meter Statistics message function +* +* @param *msg - pointer to the OpenFlow message. +* +*/ +int multi_meter_stats_reply13(uint8_t *buffer, struct ofp13_multipart_request * req) +{ + struct ofp13_meter_stats meter_stats; + struct ofp13_multipart_reply reply; + struct ofp13_meter_multipart_request *meter_stats_req = req->body; + uint32_t req_id = ntohl(meter_stats_req->meter_id); + uint8_t *buffer_ptr = buffer; + + if(req_id == OFPM13_ALL) + { + TRACE("openflow_13.c: request for all meter statistics"); + + /* Reply with all meter stats*/ + + // Count the number of meters configured, and the total number of bands + int meter_index = 0; + uint16_t bands_counter = 0; + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + bands_counter += meter_entry[meter_index]->band_count; + meter_index++; + }; + + TRACE("openflow_13.c: %d meters in meter table, %d bands", meter_index, (int)bands_counter); + + // Calculate total size - replysize + (number of meters)*statssize + (total number of bands)*bandsize + uint16_t total_size = sizeof(struct ofp13_multipart_reply) + (meter_index*sizeof(struct ofp13_meter_stats)) + (bands_counter*sizeof(struct ofp13_meter_band_stats)); + + // Format reply + reply.type = htons(OFPMP13_METER); + reply.flags = 0; // Single reply + + // Format header + reply.header.version = OF_Version; + reply.header.type = OFPT13_MULTIPART_REPLY; + reply.header.length = htons(total_size); + reply.header.xid = req->header.xid; + + // Copy reply + memcpy(buffer_ptr, &reply, sizeof(struct ofp13_multipart_reply)); + buffer_ptr += sizeof(struct ofp13_multipart_reply); + + meter_index = 0; + // Loop & format each meter stats reply + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + // Format reply with specified meter statistics + meter_stats.meter_id = htonl(meter_entry[meter_index]->meter_id); + meter_stats.len = htons(sizeof(struct ofp13_meter_stats) + (meter_entry[meter_index]->band_count*sizeof(struct ofp13_meter_band_stats))); + + meter_entry[meter_index]->flow_count = get_bound_flows(req_id); + meter_stats.flow_count = htonl(meter_entry[meter_index]->flow_count); + + meter_stats.packet_in_count = htonll(meter_entry[meter_index]->packet_in_count); + meter_stats.byte_in_count = htonll(meter_entry[meter_index]->byte_in_count); + meter_stats.duration_sec = htonl((sys_get_ms()-meter_entry[meter_index]->time_added)/1000); + meter_stats.duration_nsec = 0; // nanosecond accuracy unsupported + + // Copy configuration + memcpy(buffer_ptr, &meter_stats, sizeof(struct ofp13_meter_stats)); + buffer_ptr += sizeof(struct ofp13_meter_stats); + + // Format bands + int bands_processed = 0; + struct ofp13_meter_band_stats * ptr_buffer_band; + ptr_buffer_band = buffer_ptr; + + while(bands_processed < meter_entry[meter_index]->band_count) + { + ptr_buffer_band->packet_band_count = htonll(band_stats_array[meter_index].band_stats[bands_processed].byte_band_count); + ptr_buffer_band->byte_band_count = htonll(band_stats_array[meter_index].band_stats[bands_processed].packet_band_count); + + ptr_buffer_band++; + bands_processed++; + } + + // update buffer pointer + buffer_ptr = ptr_buffer_band; + + meter_index++; + } + + return (buffer_ptr - buffer); // return length + } + + TRACE("openflow_13.c: request for meter statistics (meter id %d)", req_id); + // Find meter entry with specified meter id + int meter_index = 0; + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + if(meter_entry[meter_index]->meter_id == req_id) + { + TRACE("of_helper.c: meter entry found - continuing"); + break; + } + + meter_index++; + } + if(meter_entry[meter_index] == NULL || meter_index == MAX_METER_13) + { + TRACE("of_helper.c: error - meter entry not found"); + + of_error13(req, OFPET13_METER_MOD_FAILED, OFPMMFC13_UNKNOWN_METER); + + return 0; // return length + } + + // Calculate total size + uint16_t total_size = sizeof(struct ofp13_multipart_reply) + sizeof(struct ofp13_meter_stats) + (meter_entry[meter_index]->band_count*sizeof(struct ofp13_meter_band_stats)); + + // Format reply + reply.type = htons(OFPMP13_METER); + reply.flags = 0; // Single reply + + // Format header + reply.header.version = OF_Version; + reply.header.type = OFPT13_MULTIPART_REPLY; + reply.header.length = htons(total_size); + reply.header.xid = req->header.xid; + + // Copy reply + memcpy(buffer_ptr, &reply, sizeof(struct ofp13_multipart_reply)); + buffer_ptr += sizeof(struct ofp13_multipart_reply); + + // Format reply with specified meter statistics + meter_stats.meter_id = htonl(req_id); + meter_stats.len = htons(total_size - sizeof(struct ofp13_multipart_reply)); + + meter_entry[meter_index]->flow_count = get_bound_flows(req_id); + meter_stats.flow_count = htonl(meter_entry[meter_index]->flow_count); + + meter_stats.packet_in_count = htonll(meter_entry[meter_index]->packet_in_count); + meter_stats.byte_in_count = htonll(meter_entry[meter_index]->byte_in_count); + meter_stats.duration_sec = htonl((sys_get_ms()-meter_entry[meter_index]->time_added)/1000); + meter_stats.duration_nsec = 0; // nanosecond accuracy unsupported + + + // Copy configuration + memcpy(buffer_ptr, &meter_stats, sizeof(struct ofp13_meter_stats)); + buffer_ptr += sizeof(struct ofp13_meter_stats); + + // Format bands + int bands_processed = 0; + struct ofp13_meter_band_stats * ptr_buffer_band; + ptr_buffer_band = buffer_ptr; + + while(bands_processed < meter_entry[meter_index]->band_count) + { + ptr_buffer_band->packet_band_count = htonll(band_stats_array[meter_index].band_stats[bands_processed].byte_band_count); + ptr_buffer_band->byte_band_count = htonll(band_stats_array[meter_index].band_stats[bands_processed].packet_band_count); + + ptr_buffer_band++; + bands_processed++; + } + + // update buffer pointer + buffer_ptr = ptr_buffer_band; + + return (buffer_ptr - buffer); // return length +} + +/* +* Main OpenFlow Meter Configuration message function +* +* @param *msg - pointer to the OpenFlow message. +* +*/ +int multi_meter_config_reply13(uint8_t *buffer, struct ofp13_multipart_request * req) +{ + struct ofp13_meter_config meter_config; + struct ofp13_multipart_reply reply; + struct ofp13_meter_multipart_request *meter_config_req = req->body; + uint32_t req_id = ntohl(meter_config_req->meter_id); + uint8_t *buffer_ptr = buffer; + + if(req_id == OFPM13_ALL) + { + TRACE("openflow_13.c: request for all meter configurations"); + + /* Reply with all meter configurations */ + + // Count the number of meters configured, and the total number of bands + int meter_index = 0; + uint16_t bands_counter = 0; + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + bands_counter += meter_entry[meter_index]->band_count; + meter_index++; + }; + + TRACE("openflow_13.c: %d meters in meter table, %d bands", meter_index, (int)bands_counter); + + // Calculate total size - replysize + (number of meters)*configsize + (total number of bands)*bandsize + uint16_t total_size = sizeof(struct ofp13_multipart_reply) + (meter_index*sizeof(struct ofp13_meter_config)) + (bands_counter*sizeof(struct ofp13_meter_band_drop)); + + // Format reply + reply.type = htons(OFPMP13_METER_CONFIG); + reply.flags = 0; // Single reply + + // Format header + reply.header.version = OF_Version; + reply.header.type = OFPT13_MULTIPART_REPLY; + reply.header.length = htons(total_size); + reply.header.xid = req->header.xid; + + // Copy reply + memcpy(buffer_ptr, &reply, sizeof(struct ofp13_multipart_reply)); + buffer_ptr += sizeof(struct ofp13_multipart_reply); + + meter_index = 0; + // Loop & format each meter config reply + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + // Format reply with specified meter configuration + meter_config.length = htons(sizeof(struct ofp13_meter_config) + (meter_entry[meter_index]->band_count*sizeof(struct ofp13_meter_band_drop))); + meter_config.flags = htons(meter_entry[meter_index]->flags); + meter_config.meter_id = htonl(meter_entry[meter_index]->meter_id); + + // Copy configuration + memcpy(buffer_ptr, &meter_config, sizeof(struct ofp13_meter_config)); + buffer_ptr += sizeof(struct ofp13_meter_config); + + // Format bands + int bands_processed = 0; + struct ofp13_meter_band_drop * ptr_band; + ptr_band = &(meter_entry[meter_index]->bands); + struct ofp13_meter_band_drop * ptr_buffer_band; + ptr_buffer_band = buffer_ptr; + + while(bands_processed < meter_entry[meter_index]->band_count) + { + ptr_buffer_band->type = htons(ptr_band->type); + ptr_buffer_band->len = htons(sizeof(struct ofp13_meter_band_drop)); + ptr_buffer_band->rate = htonl(ptr_band->rate); + ptr_buffer_band->burst_size = htonl(ptr_band->burst_size); + + ptr_buffer_band++; + ptr_band++; // Move to next band + bands_processed++; + } + + // update buffer pointer + buffer_ptr = ptr_buffer_band; + + meter_index++; + } + + return (buffer_ptr - buffer); // return length + } + + TRACE("openflow_13.c: request for meter configuration (meter id %d)", req_id); + // Find meter entry with specified meter id + int meter_index = 0; + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + if(meter_entry[meter_index]->meter_id == req_id) + { + TRACE("of_helper.c: meter entry found - continuing"); + break; + } + + meter_index++; + } + if(meter_entry[meter_index] == NULL || meter_index == MAX_METER_13) + { + TRACE("of_helper.c: error - meter entry not found"); + + of_error13(req, OFPET13_METER_MOD_FAILED, OFPMMFC13_UNKNOWN_METER); + + return 0; // return length + } + + // Calculate total size + uint16_t total_size = sizeof(struct ofp13_multipart_reply) + sizeof(struct ofp13_meter_config) + (meter_entry[meter_index]->band_count*sizeof(struct ofp13_meter_band_drop)); + + // Format reply + reply.type = htons(OFPMP13_METER_CONFIG); + reply.flags = 0; // Single reply + + // Format header + reply.header.version = OF_Version; + reply.header.type = OFPT13_MULTIPART_REPLY; + reply.header.length = htons(total_size); + reply.header.xid = req->header.xid; + + // Copy reply + memcpy(buffer_ptr, &reply, sizeof(struct ofp13_multipart_reply)); + buffer_ptr += sizeof(struct ofp13_multipart_reply); + + // Format reply with specified meter configuration + meter_config.length = htons(total_size - sizeof(struct ofp13_multipart_reply)); + meter_config.flags = htons(meter_entry[meter_index]->flags); + meter_config.meter_id = htonl(req_id); + + // Copy configuration + memcpy(buffer_ptr, &meter_config, sizeof(struct ofp13_meter_config)); + buffer_ptr += sizeof(struct ofp13_meter_config); + + // Format bands + int bands_processed = 0; + struct ofp13_meter_band_drop * ptr_band; + ptr_band = &(meter_entry[meter_index]->bands); + struct ofp13_meter_band_drop * ptr_buffer_band; + ptr_buffer_band = buffer_ptr; + + while(bands_processed < meter_entry[meter_index]->band_count) + { + ptr_buffer_band->type = htons(ptr_band->type); + ptr_buffer_band->len = htons(sizeof(struct ofp13_meter_band_drop)); + ptr_buffer_band->rate = htonl(ptr_band->rate); + ptr_buffer_band->burst_size = htonl(ptr_band->burst_size); + + ptr_buffer_band++; + ptr_band++; // Move to next band + bands_processed++; + } + + // update buffer pointer + buffer_ptr = ptr_buffer_band; + + return (buffer_ptr - buffer); // return length +} + +/* +* Main OpenFlow Meter Features message function +* +* @param *msg - pointer to the OpenFlow message. +* +*/ +int multi_meter_features_reply13(uint8_t *buffer, struct ofp13_multipart_request * req) +{ + TRACE("openflow_13.c: request for meter features"); + + struct ofp13_meter_features meter_features; + struct ofp13_multipart_reply reply; + uint8_t *buffer_ptr = buffer; + + // Format reply + reply.type = htons(OFPMP13_METER_FEATURES); + reply.flags = 0; // Single reply + + // Format header + reply.header.version = OF_Version; + reply.header.type = OFPT13_MULTIPART_REPLY; + reply.header.length = htons(sizeof(struct ofp13_meter_features) + sizeof(struct ofp13_multipart_reply)); + reply.header.xid = req->header.xid; + + // Copy reply + memcpy(buffer_ptr, &reply, sizeof(struct ofp13_multipart_reply)); + buffer_ptr += sizeof(struct ofp13_multipart_reply); + + // Format reply with meter features + meter_features.max_meter = htonl(MAX_METER_13); + meter_features.band_types = htonl(2); // Only OFPMBT_DROP supported + meter_features.capabilities = htonl(OFPMF13_KBPS | OFPMF13_PKTPS); + meter_features.max_bands = MAX_METER_BANDS_13; + meter_features.max_color = 0; + + // Copy configuration + + memcpy(buffer_ptr, &meter_features, sizeof(struct ofp13_meter_features)); + buffer_ptr += sizeof(struct ofp13_meter_features); + + return (buffer_ptr - buffer); // return length +} + /* * Main OpenFlow FLOW_MOD message function * @@ -1174,7 +1653,6 @@ void flow_add13(struct ofp_header *msg) return; } TRACE("openflow_13.c: Allocating %d bytes at %p for instruction field in flow %d", instruction_size, ofp13_oxm_inst[iLastFlow], iLastFlow+1); - //printf("openflow_13.c: Allocating %d bytes at %p for instruction field in flow %d\r\n", instruction_size, ofp13_oxm_inst[iLastFlow], iLastFlow+1); uint8_t *inst_ptr = (uint8_t *)ptr_fm + mod_size; memcpy(ofp13_oxm_inst[iLastFlow], inst_ptr, instruction_size); } else { @@ -1496,6 +1974,361 @@ void barrier13_reply(uint32_t xid) return; } +/* +* Main OpenFlow METER_MOD message function +* +* @param *msg - pointer to the OpenFlow message. +* +*/ +void meter_mod13(struct ofp_header *msg) +{ + struct ofp13_meter_mod * ptr_mm; + ptr_mm = (struct ofp13_meter_mod *) msg; + + switch(ntohs(ptr_mm->command)) + { + case OFPMC13_ADD: + meter_add13(msg); + break; + + case OFPMC13_MODIFY: + meter_modify13(msg); + break; + + case OFPMC13_DELETE: + meter_delete13(msg); + break; + } + + return; +} + +/* +* OpenFlow METER_ADD function +* +* @param *msg - pointer to the OpenFlow message. +* +*/ +void meter_add13(struct ofp_header *msg) +{ + // Check if final table entry is populated + if(meter_entry[(MAX_METER_13)-1] != NULL) + { + TRACE("openflow_13.c: unable to add meter - no more meters available"); + of_error13(msg, OFPET13_METER_MOD_FAILED, OFPMMFC13_OUT_OF_METERS); + return; + } + + struct ofp13_meter_mod * ptr_mm; + ptr_mm = (struct ofp13_meter_mod *) msg; + + // Check for existing meter + int meter_index = 0; + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + if(ntohl(ptr_mm->meter_id) == meter_entry[meter_index]->meter_id) + { + TRACE("openflow_13.c: unable to add meter - meter id already in use"); + of_error13(msg, OFPET13_METER_MOD_FAILED, OFPMMFC13_METER_EXISTS); + return; + } + + meter_index++; + } + // meter_index now holds the next available entry in the meter table + + // Find number of bands + uint16_t bands_received = ((ntohs(ptr_mm->header.length) - sizeof(struct ofp_header) - METER_PARTIAL))/sizeof(struct ofp13_meter_band_drop); // FIX + // Band list length is inferred from the length field in the header + TRACE("openflow_13.c: %d bands found in meter modification message", bands_received); + + if(bands_received > MAX_METER_BANDS_13) + { + of_error13(msg, OFPET13_METER_MOD_FAILED, OFPMMFC13_OUT_OF_BANDS); + return; + } + + // Allocate space to store meter entry + meter_entry[meter_index] = membag_alloc(sizeof(struct meter_entry13) + (bands_received * sizeof(struct ofp13_meter_band_drop))); + + // Verify memory allocation + if (meter_entry[meter_index] == NULL) + { + TRACE("openflow_13.c: unable to allocate %d bytes of memory for meter entry #%d", sizeof(struct meter_entry13) + (bands_received * sizeof(struct ofp13_meter_band_drop)), meter_index+1); + of_error13(msg, OFPET13_METER_MOD_FAILED, OFPMMFC13_OUT_OF_BANDS); + return; + } + TRACE("openflow_13.c: allocating %d bytes at %p for meter entry #%d", sizeof(struct meter_entry13) + (bands_received * sizeof(struct ofp13_meter_band_drop)), meter_entry[meter_index], meter_index+1); + + // Copy meter configs over + meter_entry[meter_index]->meter_id = ntohl(ptr_mm->meter_id); + meter_entry[meter_index]->flags = ntohs(ptr_mm->flags); + meter_entry[meter_index]->band_count = bands_received; + + // Initialise time added + meter_entry[meter_index]->time_added = sys_get_ms(); + + // Copy bands over + if(bands_received != 0) + { + struct ofp13_meter_band_drop * ptr_band; + uint16_t bands_processed = 0; + + // Initialise pointer to first meter band destination + ptr_band = &(meter_entry[meter_index]->bands); + struct ofp13_meter_band_drop * ptr_rxband; + ptr_rxband = &(ptr_mm->bands); + + do + { + // Copy individual band + //memcpy((ptr_band + band_size*bands_processed), ((ptr_mm->bands) + band_size*bands_processed), PADDED_BAND_LEN); + //ptr_band->type = ntohs(ptr_mm->bands[bands_processed].type); + //ptr_band->len = ntohs(ptr_mm->bands[bands_processed].len); + //ptr_band->rate = ntohl(ptr_mm->bands[bands_processed].rate); + //ptr_band->burst_size = ntohl(ptr_mm->bands[bands_processed].burst_size); + + ptr_band->type = ntohs(ptr_rxband->type); + ptr_band->len = ntohs(ptr_rxband->len); + ptr_band->rate = ntohl(ptr_rxband->rate); + ptr_band->burst_size = ntohl(ptr_rxband->burst_size); + + ptr_band++; // Move to next band storage location + ptr_rxband++; // Move to next received band + bands_processed++; + + // ***** TODO : add error checking for band processing + TRACE("openflow_13.c: %d of %d bands processed", bands_processed, bands_received); + + } while (bands_processed < bands_received); + } + + iLastMeter++; // Decrement last meter count + + return; +} + +/* +* OpenFlow METER_MODIFY function +* +* @param *msg - pointer to the OpenFlow message. +* +*/ +void meter_modify13(struct ofp_header *msg) +{ + struct ofp13_meter_mod * ptr_mm; + ptr_mm = (struct ofp13_meter_mod *) msg; + uint32_t req_id = ntohl(ptr_mm->meter_id); + + TRACE("openflow_13.c: meter modify message (meter id %d)", req_id); + // Find meter entry with specified meter id + int meter_index = 0; + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + if(meter_entry[meter_index]->meter_id == req_id) + { + TRACE("of_helper.c: meter entry found - continuing"); + break; + } + + meter_index++; + } + if(meter_entry[meter_index] == NULL || meter_index == MAX_METER_13) + { + TRACE("of_helper.c: error - meter entry not found"); + + of_error13(msg, OFPET13_METER_MOD_FAILED, OFPMMFC13_UNKNOWN_METER); + + return; // return length + } + + // Find number of bands in received entry + uint16_t bands_received = ((ntohs(ptr_mm->header.length) - sizeof(struct ofp_header) - METER_PARTIAL))/sizeof(struct ofp13_meter_band_drop); + // Band list length is inferred from the length field in the header + TRACE("openflow_13.c: %d bands found in meter modification message", bands_received); + + if(bands_received > MAX_METER_BANDS_13) + { + of_error13(msg, OFPET13_METER_MOD_FAILED, OFPMMFC13_OUT_OF_BANDS); + return; + } + + // Store the top-level meter statistics + struct meter_entry13 entry_save = {0}; + entry_save = *meter_entry[meter_index]; + + // Free allocated memory + membag_free(meter_entry[meter_index]); + + /* Delete band counters */ + // Create temporary empty structure + struct meter_band_stats_array empty_stats_array = {0}; + // Copy over the existing structure + band_stats_array[meter_index] = empty_stats_array; + + // Allocate space to store modified meter entry + meter_entry[meter_index] = membag_alloc(sizeof(struct meter_entry13) + (bands_received * sizeof(struct ofp13_meter_band_drop))); + + // Verify memory allocation + if (meter_entry[meter_index] == NULL) + { + TRACE("openflow_13.c: unable to allocate %d bytes of memory for meter entry #%d", sizeof(struct meter_entry13) + (bands_received * sizeof(struct ofp13_meter_band_drop)), meter_index+1); + of_error13(msg, OFPET13_METER_MOD_FAILED, OFPMMFC13_OUT_OF_BANDS); + return; + } + TRACE("openflow_13.c: allocating %d bytes at %p for meter entry #%d", sizeof(struct meter_entry13) + (bands_received * sizeof(struct ofp13_meter_band_drop)), meter_entry[meter_index], meter_index+1); + + // Restore top-level statistics + *meter_entry[meter_index] = entry_save; + + // Update modified configs + meter_entry[meter_index]->flags = ntohs(ptr_mm->flags); + meter_entry[meter_index]->band_count = bands_received; + + // Copy bands over + if(bands_received != 0) + { + struct ofp13_meter_band_drop * ptr_band; + uint16_t bands_processed = 0; + + // Initialise pointer to first meter band destination + ptr_band = &(meter_entry[meter_index]->bands); + struct ofp13_meter_band_drop * ptr_rxband; + ptr_rxband = &(ptr_mm->bands); + + do + { + // Copy individual band + //memcpy((ptr_band + band_size*bands_processed), ((ptr_mm->bands) + band_size*bands_processed), PADDED_BAND_LEN); + //ptr_band->type = ntohs(ptr_mm->bands[bands_processed].type); + //ptr_band->len = ntohs(ptr_mm->bands[bands_processed].len); + //ptr_band->rate = ntohl(ptr_mm->bands[bands_processed].rate); + //ptr_band->burst_size = ntohl(ptr_mm->bands[bands_processed].burst_size); + + ptr_band->type = ntohs(ptr_rxband->type); + ptr_band->len = ntohs(ptr_rxband->len); + ptr_band->rate = ntohl(ptr_rxband->rate); + ptr_band->burst_size = ntohl(ptr_rxband->burst_size); + + // ***** TODO : add error checking for band processing + TRACE("openflow_13.c: %d of %d bands processed", bands_processed, bands_received); + + ptr_band++; // Move to next band storage location + ptr_rxband++; // Move to next received band + bands_processed++; + } while (bands_processed < bands_received); + } + + return; +} + +/* +* OpenFlow METER_DELETE function +* +* @param *msg - pointer to the OpenFlow message. +* +*/ +void meter_delete13(struct ofp_header *msg) +{ + struct ofp13_meter_mod * ptr_mm; + ptr_mm = (struct ofp13_meter_mod *) msg; + + // Check if all meters need to be deleted + if(ntohl(ptr_mm->meter_id) == OFPM13_ALL) + { + TRACE("openflow_13.c: request to delete all meters"); + + int meter_index = 0; + + // Create temporary empty structure + struct meter_band_stats_array empty_stats_array = {0}; + + // Loop through all meters + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13) + { + /* Delete entry */ + // Free allocated memory + membag_free(meter_entry[meter_index]); + // Clear the pointer + meter_entry[meter_index] = NULL; + + /* Delete band counters */ + // Copy over the existing structure + band_stats_array[meter_index] = empty_stats_array; + + meter_index++; + } + + return; + } + + TRACE("openflow_13.c: request to DELETE meter_id %d", ntohl(ptr_mm->meter_id)); + + int meter_index = 0; + int meter_location = -1; + // Loop through existing meters + while(meter_entry[meter_index] != NULL && meter_index < MAX_METER_13 && meter_location == -1) + { + // Compare requested meter_id with entry's meter_id + if(ntohl(ptr_mm->meter_id) == meter_entry[meter_index]->meter_id) + { + // Store the index + meter_location = meter_index; + } + + meter_index++; + } + + if(meter_location == -1) + { + TRACE("openflow_13.c: meter_id not found"); + // No error message required + return; + } + + /* Delete entry */ + // Free allocated memory + membag_free(meter_entry[meter_location]); + // Clear the pointer + meter_entry[meter_location] = NULL; + meter_index = meter_location; + + /* Delete band counters */ + // Create temporary empty structure + struct meter_band_stats_array empty_stats_array = {0}; + // Copy over the existing structure + band_stats_array[meter_index] = empty_stats_array; + + // Consolidate table + if(meter_entry[meter_index+1] == NULL) + { + TRACE("openflow_13.c: meter table consolidation not required - no trailing entries"); + } + else + { + TRACE("openflow_13.c: consolidating meter table"); + // Increment the index until the last meter entry is found + while(meter_entry[meter_index+1] != NULL && (meter_index+1) < MAX_METER_13) + { + meter_index++; + } + meter_entry[meter_location] = meter_entry[meter_index]; // Move last entry into deleted entry location + meter_entry[meter_index] = 0; // Zero the moved entry + + /* Consolidate meter bands */ + // Copy last meter's band counters into the deleted entry's band counters + band_stats_array[meter_location] = band_stats_array[meter_index]; + // Zero the moved band counters + band_stats_array[meter_index] = empty_stats_array; + + TRACE("openflow_13.c: meter table contains %d meter entries", meter_index); + } + + iLastMeter--; // Decrement last meter count + + return; +} + /* * OpenFlow ERROR message function * diff --git a/ZodiacFX/src/openflow_spec/openflow_spec13.h b/ZodiacFX/src/openflow_spec/openflow_spec13.h index 4b01338..7905b94 100644 --- a/ZodiacFX/src/openflow_spec/openflow_spec13.h +++ b/ZodiacFX/src/openflow_spec/openflow_spec13.h @@ -449,6 +449,29 @@ enum ofp13_flow_mod_failed_code { OFPFMFC13_BAD_FLAGS = 7, /* Unsupported or unknown flags. */ }; +/* ofp_error_msg 'code' values for OFPET_METER_MOD_FAILED. 'data' contains + * at least the first 64 bytes of the failed request. */ +enum ofp13_meter_mod_failed_code { + OFPMMFC13_UNKNOWN = 0, /* Unspecified error. */ + OFPMMFC13_METER_EXISTS = 1, /* Meter not added because a Meter ADD + * attempted to replace an existing Meter. */ + OFPMMFC13_INVALID_METER = 2, /* Meter not added because Meter specified + * is invalid, + * or invalid meter in meter action. */ + OFPMMFC13_UNKNOWN_METER = 3, /* Meter not modified because a Meter MODIFY + * attempted to modify a non-existent Meter, + * or bad meter in meter action. */ + OFPMMFC13_BAD_COMMAND = 4, /* Unsupported or unknown command. */ + OFPMMFC13_BAD_FLAGS = 5, /* Flag configuration unsupported. */ + OFPMMFC13_BAD_RATE = 6, /* Rate unsupported. */ + OFPMMFC13_BAD_BURST = 7, /* Burst size unsupported. */ + OFPMMFC13_BAD_BAND = 8, /* Band unsupported. */ + OFPMMFC13_BAD_BAND_VALUE = 9, /* Band value unsupported. */ + OFPMMFC13_OUT_OF_METERS = 10, /* No more meters available. */ + OFPMMFC13_OUT_OF_BANDS = 11, /* The maximum number of properties + * for a meter has been exceeded. */ +}; + /* ## ----------------- ## */ /* ## OpenFlow Actions. ## */ /* ## ----------------- ## */ @@ -780,6 +803,135 @@ struct ofp13_port_status { struct ofp13_port desc; }; +/* Meter numbering. Flow meters can use any number up to OFPM_MAX. */ +enum ofp13_meter { + /* Last usable meter. */ + OFPM13_MAX = 0xffff0000, + + /* Virtual meters. */ + OFPM13_SLOWPATH = 0xfffffffd, /* Meter for slow datapath. */ + OFPM13_CONTROLLER = 0xfffffffe, /* Meter for controller connection. */ + OFPM13_ALL = 0xffffffff, /* Represents all meters for stat requests + commands. */ +}; + +/* Meter band types */ +enum ofp13_meter_band_type { + OFPMBT13_DROP = 1, /* Drop packet. */ + OFPMBT13_DSCP_REMARK = 2, /* Remark DSCP in the IP header. */ + OFPMBT13_EXPERIMENTER = 0xFFFF /* Experimenter meter band. */ +}; + +/* Common header for all meter bands */ +struct ofp13_meter_band_header { + uint16_t type; /* One of OFPMBT_*. */ + uint16_t len; /* Length in bytes of this band. */ + uint32_t rate; /* Rate for this band. */ + uint32_t burst_size; /* Size of bursts. */ +}; + +/* OFPMBT_DROP band - drop packets */ +struct ofp13_meter_band_drop { + uint16_t type; /* OFPMBT_DROP. */ + uint16_t len; /* Length in bytes of this band. */ + uint32_t rate; /* Rate for dropping packets. */ + uint32_t burst_size; /* Size of bursts. */ + uint8_t pad[4]; +}; + +/* OFPMBT_DSCP_REMARK band - Remark DSCP in the IP header */ +struct ofp_meter_band_dscp_remark { + uint16_t type; /* OFPMBT_DSCP_REMARK. */ + uint16_t len; /* Length in bytes of this band. */ + uint32_t rate; /* Rate for remarking packets. */ + uint32_t burst_size; /* Size of bursts. */ + uint8_t prec_level; /* Number of drop precedence level to add. */ + uint8_t pad[3]; +}; + +/* OFPMBT_EXPERIMENTER band - Experimenter type. + * The rest of the band is experimenter-defined. */ +struct ofp13_meter_band_experimenter { + uint16_t type; /* One of OFPMBT_*. */ + uint16_t len; /* Length in bytes of this band. */ + uint32_t rate; /* Rate for this band. */ + uint32_t burst_size; /* Size of bursts. */ + uint32_t experimenter; /* Experimenter ID which takes the same + form as in struct + ofp_experimenter_header. */ +}; + +/* Meter commands */ +enum ofp13_meter_mod_command { + OFPMC13_ADD, /* New meter. */ + OFPMC13_MODIFY, /* Modify specified meter. */ + OFPMC13_DELETE, /* Delete specified meter. */ +}; + +/* Meter configuration flags */ +enum ofp13_meter_flags { + OFPMF13_KBPS = 1 << 0, /* Rate value in kb/s (kilo-bit per second). */ + OFPMF13_PKTPS = 1 << 1, /* Rate value in packet/sec. */ + OFPMF13_BURST = 1 << 2, /* Do burst size. */ + OFPMF13_STATS = 1 << 3, /* Collect statistics. */ +}; + +/* Meter configuration. OFPT_METER_MOD. */ +struct ofp13_meter_mod { + struct ofp_header header; + uint16_t command; /* One of OFPMC_*. */ + uint16_t flags; /* Bitmap of OFPMF_* flags. */ + uint32_t meter_id; /* Meter instance. */ + struct ofp13_meter_band_header bands[0]; /* The band list length is + inferred from the length field + in the header. */ +}; + +/* Body of OFPMP_METER and OFPMP_METER_CONFIG requests. */ +struct ofp13_meter_multipart_request { + uint32_t meter_id; /* Meter instance, or OFPM_ALL. */ + uint8_t pad[4]; /* Align to 64 bits. */ +}; + +/* Statistics for each meter band */ +struct ofp13_meter_band_stats { + uint64_t packet_band_count; /* Number of packets in band. */ + uint64_t byte_band_count; /* Number of bytes in band. */ +}; + +/* Body of reply to OFPMP_METER request. Meter statistics. */ +struct ofp13_meter_stats { + uint32_t meter_id; /* Meter instance. */ + uint16_t len; /* Length in bytes of this stats. */ + uint8_t pad[6]; + uint32_t flow_count; /* Number of flows bound to meter. */ + uint64_t packet_in_count; /* Number of packets in input. */ + uint64_t byte_in_count; /* Number of bytes in input. */ + uint32_t duration_sec; /* Time meter has been alive in seconds. */ + uint32_t duration_nsec; /* Time meter has been alive in nanoseconds beyond + duration_sec. */ + struct ofp13_meter_band_stats band_stats[0]; /* The band_stats length is + inferred from the length field. */ +}; + +/* Body of reply to OFPMP_METER_CONFIG request. Meter configuration. */ +struct ofp13_meter_config { + uint16_t length; /* Length of this entry. */ + uint16_t flags; /* All OFPMF_* that apply. */ + uint32_t meter_id; /* Meter instance. */ + struct ofp13_meter_band_header bands[0]; /* The bands length is + inferred from the length field. */ +}; + +/* Body of reply to OFPMP_METER_FEATURES request. Meter features. */ +struct ofp13_meter_features { + uint32_t max_meter; /* Maximum number of meters. */ + uint32_t band_types; /* Bitmaps of (1 << OFPMBT_*) values supported. */ + uint32_t capabilities; /* Bitmaps of "ofp_meter_flags". */ + uint8_t max_bands; /* Maximum bands per meters */ + uint8_t max_color; /* Maximum color value */ + uint8_t pad[2]; +}; /* ## -------------------------- ## */ /* ## OpenFlow Extensible Match. ## */