diff --git a/cmd_parser.c b/cmd_parser.c index fc57c75..3794f2e 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -95,15 +95,16 @@ uint8_t cmd_compare(uint8_t start, uint8_t * __code cmd) for (i = cmd_words_b[start]; i != cmd_words_b[start + 1] && cmd_buffer[i] != ' '; i++) { i &= SBUF_SIZE - 1; -// print_short(i); write_char(':'); print_short(j); write_char('#'); print_string("\n"); +// print_byte(i); write_char(':'); print_byte(j); write_char('#'); print_string("\n"); // write_char('>'); write_char(cmd[j]); write_char('-'); write_char(cmd_buffer[i]); print_string("\n"); - if (!cmd[j]) + if (!cmd[j] && !isletter(cmd_buffer[i])) return 1; if (cmd_buffer[i] != cmd[j++]) break; } -// write_char('.'); print_short(i); write_char(':'); print_short(i); - if (i == cmd_words_b[start + 1] || cmd_buffer[i] == ' ') +// write_char('.'); print_byte(i); write_char(':'); print_byte(j); write_char(','); print_byte (cmd[j-1]); +// write_char(','); print_byte(cmd[j]); + if (i == cmd_words_b[start + 1] || (cmd_buffer[i] == ' ' && !cmd[j])) return 1; return 0; } @@ -186,28 +187,100 @@ uint8_t parse_ip(register uint8_t idx) } -void parse_trunk(void) +void parse_lag(void) { __xdata uint8_t group; __xdata uint16_t members = 0; + if (cmd_words_b[1] > 0 && cmd_compare(1, "show")) { + print_string("LAG status:\n"); + for (uint8_t i = 0; i < 4; i++) { + write_char(' '); write_char('1' + i); + reg_read_m(RTL837X_TRK_MBR_CTRL_BASE + (i << 2)); + members = ((uint16_t)sfr_data[2]) << 8 | sfr_data[3]; + if (!members) { + print_string(" disabled\n"); + continue; + } + print_string(" member ports: "); + for (uint8_t j = 0; j < 10; j++) { + if (members & 1) { + if (!isRTL8373) + write_char('0' + log_to_phys_port[j]); + else + write_char('1' + j); + write_char(' '); + } + members >>= 1; + } + print_string(" (hash: 0x"); + reg_read_m(RTL837X_TRK_HASH_CTRL_BASE + (i << 2)); + print_byte(sfr_data[3]); + print_string(")\n"); + } + return; + } + + if (cmd_words_b[2] <= 0 || !isnumber(cmd_buffer[cmd_words_b[1]])) + goto err; group = cmd_buffer[cmd_words_b[1]] - '0'; uint8_t w = 2; - while (cmd_words_b[w] > 0) { + while (cmd_words_b[w + 1] > 0) { +// write_char('|'); print_byte(w); write_char(':'); write_char(cmd_buffer[cmd_words_b[w]]); write_char('-'); uint8_t port; if (isnumber(cmd_buffer[cmd_words_b[w]])) { port = cmd_buffer[cmd_words_b[w]] - '1'; - if (port > maxPort) - goto err; - members |= ((uint16_t)1) << port; + if (isnumber(cmd_buffer[cmd_words_b[w] + 1])) + port = (port + 1) * 10 + cmd_buffer[cmd_words_b[w] + 1] - '1'; + if (!isRTL8373) + port = phys_to_log_port[port]; + } else { + goto err; + } + if (port > maxPort) + goto err; + members |= ((uint16_t)1) << port; + w++; + } + port_lag_members_set(group, members); + return; +err: + print_string("Error: lag [port]..."); +} + + +void parse_lag_hash(void) +{ + __xdata uint8_t group; + __xdata uint8_t hash = 0; + + group = cmd_buffer[cmd_words_b[1]] - '0'; + + uint8_t w = 2; + while (cmd_words_b[w + 1] > 0) { + if (cmd_compare(w, "spa")) + hash |= LAG_HASH_SOURCE_PORT_NUMBER; + else if (cmd_compare(w, "smac")) + hash |= LAG_HASH_L2_SMAC; + else if (cmd_compare(w, "dmac")) + hash |= LAG_HASH_L2_DMAC; + else if (cmd_compare(w, "sip")) + hash |= LAG_HASH_L3_SIP; + else if (cmd_compare(w, "dip")) + hash |= LAG_HASH_L3_DIP; + else if (cmd_compare(w, "sport")) + hash |= LAG_HASH_L4_SPORT; + else if (cmd_compare(w, "dport")) + hash |= LAG_HASH_L4_DPORT; + else { + print_string("Error: invalid hash type:"); + print_string_x(&cmd_buffer[cmd_words_b[w]]); + write_char('\n'); } w++; } - trunk_set(group, members); - return; -err: - print_string("Error: trunk [port]..."); + port_lag_hash_set(group, hash); } @@ -553,8 +626,7 @@ void cmd_parser(void) __banked if (cmd_compare(0, "reset")) { print_string("\nRESET\n\n"); reset_chip(); - } - if (cmd_compare(0, "sfp")) { + } else if (cmd_compare(0, "sfp")) { uint8_t rate = sfp_read_reg(0, 12); print_string("\nRate: "); print_byte(rate); print_string(" Encoding: "); print_byte(sfp_read_reg(0, 11)); @@ -564,11 +636,9 @@ void cmd_parser(void) __banked if (c) write_char(c); } - } - if (cmd_compare(0, "stat")) { + } else if (cmd_compare(0, "stat")) { port_stats_print(); - } - if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'r') { + } else if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'r') { print_string("\nPRINT SECURITY REGISTERS\n"); // The following will only show something else than 0xff if it was programmed for a managed switch flash_region.addr = 0x0001000; @@ -580,44 +650,36 @@ void cmd_parser(void) __banked flash_region.addr = 0x0003000; flash_region.len = 40; flash_read_security(); - } - if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'd') { + } else if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'd') { print_string("\nDUMPING FLASH\n"); flash_region.addr = 0; flash_region.len = 255; flash_dump(255); - } - if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'j') { + } else if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'j') { print_string("\nJEDEC ID\n"); flash_read_jedecid(); - } - if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'u') { + } else if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'u') { print_string("\nUNIQUE ID\n"); flash_read_uid(); - } - // Switch to flash 62.5 MHz mode - if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 's') { - print_string("\nFLASH FAST MODE\n"); + } else if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 's') { + print_string("\nFLASH FAST MODE\n"); // Switch to flash 62.5 MHz mode flash_init(1); print_string("\nNow dumping flash\n"); flash_region.addr = 0; flash_region.len = 255; flash_dump(255); - } - if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'e') { + } else if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'e') { print_string("\nFLASH erase\n"); flash_region.addr = 0x20000; flash_sector_erase(); - } - if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'w') { + } else if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && cmd_buffer[cmd_words_b[1]] == 'w') { print_string("\nFLASH write\n"); for (uint8_t i = 0; i < 20; i++) flash_buf[i] = greeting[i]; flash_region.addr = 0x200000; flash_region.len = 20; flash_write_bytes(flash_buf); - } - if (cmd_compare(0, "port") && cmd_words_b[1] > 0) { + } else if (cmd_compare(0, "port") && cmd_words_b[1] > 0) { print_string("\nPORT "); uint8_t p = cmd_buffer[cmd_words_b[1]] - '1'; print_byte(p); @@ -637,8 +699,7 @@ void cmd_parser(void) __banked print_string(" OFF\n"); phy_set_mode(p, PHY_OFF, 0, 0); } - } - if (cmd_compare(0, "ip")) { + } else if (cmd_compare(0, "ip")) { print_string("Got ip command: "); if (!parse_ip(cmd_words_b[1])) uip_ipaddr(&uip_hostaddr, ip[0], ip[1], ip[2], ip[3]); @@ -646,8 +707,7 @@ void cmd_parser(void) __banked print_string("Invalid IP address\n"); print_byte(ip[0]); print_byte(ip[1]); print_byte(ip[2]); print_byte(ip[3]); write_char('\n'); - } - if (cmd_compare(0, "gw")) { + } else if (cmd_compare(0, "gw")) { print_string("Got gw command: "); if (!parse_ip(cmd_words_b[1])) uip_ipaddr(&uip_draddr, ip[0], ip[1], ip[2], ip[3]); @@ -655,8 +715,7 @@ void cmd_parser(void) __banked print_string("Invalid IP address\n"); print_byte(ip[0]); print_byte(ip[1]); print_byte(ip[2]); print_byte(ip[3]); write_char('\n'); - } - if (cmd_compare(0, "netmask")) { + } else if (cmd_compare(0, "netmask")) { print_string("Got netmask command: "); if (!parse_ip(cmd_words_b[1])) uip_ipaddr(&uip_netmask, ip[0], ip[1], ip[2], ip[3]); @@ -664,14 +723,12 @@ void cmd_parser(void) __banked print_string("Invalid IP address\n"); print_byte(ip[0]);print_byte(ip[1]);print_byte(ip[2]);print_byte(ip[3]); write_char('\n'); - } - if (cmd_compare(0, "l2")) { + } else if (cmd_compare(0, "l2")) { if (cmd_words_b[1] > 0 && cmd_compare(1, "forget")) port_l2_forget(); else port_l2_learned(); - } - if (cmd_compare(0, "stp")) { + } else if (cmd_compare(0, "stp")) { if (cmd_words_b[1] > 0 && cmd_compare(1, "on")) { print_string("STP enabled\n"); stpEnabled = 1; @@ -681,8 +738,7 @@ void cmd_parser(void) __banked stp_off(); stpEnabled = 0; } - } - if (cmd_compare(0, "pvid") && cmd_words_b[1] > 0 && cmd_words_b[2] > 0) { + } else if (cmd_compare(0, "pvid") && cmd_words_b[1] > 0 && cmd_words_b[2] > 0) { __xdata uint16_t pvid; uint8_t port; port = cmd_buffer[cmd_words_b[1]] - '1'; @@ -690,35 +746,27 @@ void cmd_parser(void) __banked port = phys_to_log_port[port]; if (!atoi_short(&pvid, cmd_words_b[2])) port_pvid_set(port, pvid); - } - if (cmd_compare(0, "vlan")) { + } else if (cmd_compare(0, "vlan")) { parse_vlan(); - } - if (cmd_compare(0, "mirror")) { + } else if (cmd_compare(0, "mirror")) { parse_mirror(); - } - if (cmd_compare(0, "trunk")) { - parse_trunk(); - } - if (cmd_compare(0, "sds")) { + } else if (cmd_compare(0, "lag")) { + parse_lag(); + } else if (cmd_compare(0, "laghash")) { + parse_lag_hash(); + } else if (cmd_compare(0, "sds")) { print_reg(RTL837X_REG_SDS_MODES); - } - if (cmd_compare(0, "gpio")) { + } else if (cmd_compare(0, "gpio")) { print_gpio_status(); - } - if (cmd_compare(0, "regget")) { + } else if (cmd_compare(0, "regget")) { parse_regget(); - } - if (cmd_compare(0, "regset")) { + } else if (cmd_compare(0, "regset")) { parse_regset(); - } - if (cmd_compare(0, "rnd")) { + } else if (cmd_compare(0, "rnd")) { parse_rnd(); - } - if (cmd_compare(0, "passwd")) { + } else if (cmd_compare(0, "passwd")) { parse_passwd(); - } - if (cmd_compare(0, "eee")) { + } else if (cmd_compare(0, "eee")) { int8_t port = -1; if (cmd_words_b[3] > 0) { port = cmd_buffer[cmd_words_b[2]] - '1'; @@ -741,17 +789,15 @@ void cmd_parser(void) __banked else port_eee_status_all(); } - } - if (cmd_compare(0, "version")) { + } else if (cmd_compare(0, "version")) { print_sw_version(); - } - if (cmd_compare(0, "history")) { + } else if (cmd_compare(0, "history")) { __xdata uint16_t p = (cmd_history_ptr + 1) & CMD_HISTORY_MASK; __xdata uint8_t found_begin = 0; - print_string("History ptr: "); - print_short(cmd_history_ptr); write_char('\n'); +// print_string("History ptr: "); +// print_short(cmd_history_ptr); write_char('\n'); while (p != cmd_history_ptr) { - print_short(p); write_char(' '); +// print_short(p); write_char(' '); if (!cmd_history[p] || cmd_history[p] == '\n') found_begin = 1; if (found_begin && cmd_history[p]) @@ -793,8 +839,6 @@ void execute_config(void) __banked do { flash_region.addr = pos; flash_region.len = FLASH_READ_BURST_SIZE; - write_char('-'); print_long(flash_region.addr); write_char(':'); print_short(flash_region.len); write_char('\n'); - flash_read_bulk(flash_buf); uint8_t cfg_idx = 0; @@ -802,11 +846,8 @@ void execute_config(void) __banked do { for (uint8_t cmd_idx = 0; cmd_idx < (SBUF_SIZE - 1); cmd_idx++) { c = flash_buf[cfg_idx++]; - print_byte(c); - if (c == 0 || c == '\n') { cmd_buffer[cmd_idx] = '\0'; - write_char('\n'); write_char('#'); print_short(cfg_idx); write_char('-'); print_short(cmd_idx); write_char('-'); print_string_x(cmd_buffer); write_char('\n'); if (cmd_idx && !cmd_tokenize()) cmd_parser(); if (c == 0) @@ -816,7 +857,6 @@ void execute_config(void) __banked cmd_buffer[cmd_idx] = c; } - write_char('N'); } while(cfg_idx); len_left -= FLASH_READ_BURST_SIZE; diff --git a/doc/images/LAG_config.png b/doc/images/LAG_config.png new file mode 100644 index 0000000..c02dac7 Binary files /dev/null and b/doc/images/LAG_config.png differ diff --git a/doc/link_aggregation.md b/doc/link_aggregation.md new file mode 100644 index 0000000..e38600d --- /dev/null +++ b/doc/link_aggregation.md @@ -0,0 +1,227 @@ +# Link Aggregation (aka Trunking) + +The RTL827x allows to combine multiple ports to a single logical link +(Link Aggregation / Trunking) according to IEEE 802.3ad. LAGs allow to +combine the individual physical links into a single link with the combined +throughput and automatic redundancy when one of the link fails. +Up to 4 Link Aggregation Groups (LAGs) can be defined on the switch devices. + +## LAG control +Four registers `RTL837X_TRK_MBR_CTRL_BASE(lag) (0x4f38-0x4f44)` define the LAG membership +via a port mask of the logical port numbers. + +A hash algorithm applied to L2, L3 and L4 properties of a packet are used to decide which +of the links (ports) is being used to transfer the packet. The possible properties used in the +hash are: +``` +#define LAG_HASH_SOURCE_PORT_NUMBER 0x01 +#define LAG_HASH_L2_SMAC 0x02 +#define LAG_HASH_L2_DMAC 0x04 +#define LAG_HASH_L3_SIP 0x08 +#define LAG_HASH_L3_DIP 0x10 +#define LAG_HASH_L4_SPORT 0x20 +#define LAG_HASH_L4_DPORT 0x40 +#define LAG_HASH_DEFAULT (LAG_HASH_L2_SMAC | LAG_HASH_L2_DMAC | LAG_HASH_L3_SIP | LAG_HASH_L3_DIP | LAG_HASH_L4_SPORT | LAG_HASH_L4_DPORT) +``` +The hash algorithm used to select links (exit ports) is defined for each LAG individually in +`RTL837X_TRK_HASH_CTRL_BASE (0x4f48-0x4f54)`. + +## Trunking API +The code currently provides the following functions: +``` +/* + * Configure LAGs + * Sets the members via port bitmask of a given Link Aggregation Group + * The groups have numbers 0-3 + * The bitmask represents up to 10 ports + * If currently no LAG has algorithm used, a default is applied + */ +void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banked; + +/* + * Configures the hash algorithm used for a LAG + * lag is the Group to configure and hash is a bitmask + */ +void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash_bits) __banked; +``` + +## LAG configuration on the Serial Console +For testing the following commands are provided on the serial console: +``` +> lag [p1] [p2]... + Create or set a LAG. Trunk-ID is 1 or 2. Ports are physical ports + If only the LAG-ID is given but no members, the LAG is deleted + +> lag show + Shows information on all 4 lags + +> laghash 0 [hash1] [hash2]... + Uses the given packet properties when hashing the packet to select the link + Names for the hashes are spa, smac, dmac, sip, dip, sport, dport +``` +When a lag is creates, by default the hash is based on smac, dmac, sip, dip, sport, dport. When you +use your own hash settings, make sure that the hash always uses both the source and destination +property of the packet, as otherwise pakets will not be routed symmetrically. + +## LAG configuration via the Web Interface +In the web-interface select Link Aggregation in the left navigation panel. The page will look like this: +![Alt text](images/LAG_config.png?raw=true "Link Aggregation Web-Page") +Each of th 4 LAGs is configured separately. After the web-page has loaded, the current configuration +can be edited by clicking on the port-images to include that port or exclude it from a LAG. +When pressing on the Create/Update button, the LAG will be automatically created if not yet done, or +updated. If a lage is updated to not having any members, then it is effectively deleted. + +All LAGs are created with the default hash-function (see above). This currently cannot be changed +from the Web. + +## A Test using a single Linux Desktop +The following is a simple test using 2 RTL 2.5 GBit switches with at least 1 SFP+-port each. You +will also need 4 10GBit SFP+ modules (DAC or Fiber) and 2 SFP+ ports on your desktop. + +The following shows the network configuration +``` + ----------------- ----------------- + Linux Comuter | | 2.5 GBit | | same Linux Computer + ---------- 10G | P1 |------------| P1 | 10G ---------- +192.168.9.1 | SFP+ |==========| Switch 1 | 2.5 GBit | Switch 2 |==========| SFP+ | 192.168.9.2 + enp1s0f0 ---------- | P2 |------------| P2 | ---------- enp1s0f1 + | | | | + ------------------ ----------------- +``` + +On _both_ switches create a LAG with ports 1 and 2 inside and the default hash algorithm which takes +source and destination ports into account, e.g. just use the default: +``` +> lag 0 1 2 +``` + + +The following shows the configuration on the desktop using a dual 10GBit card with 2 SFP+ modules: +``` +[234690.755634] ixgbe: Intel(R) 10 Gigabit PCI Express Network Driver +[234690.755637] ixgbe: Copyright (c) 1999-2016 Intel Corporation. +[234690.921614] ixgbe 0000:01:00.0: Multiqueue Enabled: Rx Queue count = 12, Tx Queue count = 12 XDP Queue count = 0 +[234690.921914] ixgbe 0000:01:00.0: 32.000 Gb/s available PCIe bandwidth (5.0 GT/s PCIe x8 link) +[234690.921999] ixgbe 0000:01:00.0: MAC: 2, PHY: 19, SFP+: 5, PBA No: FFFFFF-0FF +[234690.922002] ixgbe 0000:01:00.0: 28:41:c6:xx:xx:aa +[234690.924946] ixgbe 0000:01:00.0: Intel(R) 10 Gigabit Network Connection +[234690.990024] ixgbe 0000:01:00.0 enp1s0f0: renamed from eth0 +[234691.056447] ixgbe 0000:01:00.0: registered PHC device on enp1s0f0 +[234691.089417] ixgbe 0000:01:00.1: Multiqueue Enabled: Rx Queue count = 12, Tx Queue count = 12 XDP Queue count = 0 +[234691.089706] ixgbe 0000:01:00.1: 32.000 Gb/s available PCIe bandwidth (5.0 GT/s PCIe x8 link) +[234691.089788] ixgbe 0000:01:00.1: MAC: 2, PHY: 19, SFP+: 18, PBA No: FFFFFF-0FF +[234691.089790] ixgbe 0000:01:00.1: 28:41:c6:xx:xx:ab +[234691.160997] ixgbe 0000:01:00.1: Intel(R) 10 Gigabit Network Connection +[234691.166102] ixgbe 0000:01:00.1 enp1s0f1: renamed from eth0 +[234691.231579] ixgbe 0000:01:00.1: registered PHC device on enp1s0f1 +[234691.236965] ixgbe 0000:01:00.0 enp1s0f0: detected SFP+: 5 +[234691.485031] ixgbe 0000:01:00.0 enp1s0f0: NIC Link is Up 10 Gbps, Flow Control: RX/TX +[234691.557003] ixgbe 0000:01:00.1 enp1s0f1: detected SFP+: 18 +[234691.753061] ixgbe 0000:01:00.1 enp1s0f1: NIC Link is Up 10 Gbps, Flow Control: RX/TX +``` + +Now set up 2 network namespaces and put each interface inside one: +``` +sudo ip netns add netns_eth0 +sudo ip netns add netns_eth1 +sudo ip link set enp1s0f0 netns netns_eth0 +sudo ip link set enp1s0f1 netns netns_eth1 +``` + +Configure network interface addresses 192.168.9.2 and 192.168.9.1 in each namespace: +``` +sudo ip netns exec netns_eth0 ifconfig enp1s0f0 192.168.9.1 netmask 255.255.255.0 + +sudo ip netns exec netns_eth0 ip a +1: lo: mtu 65536 qdisc noop state DOWN group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 +24: enp1s0f0: mtu 1500 qdisc mq state UP group default qlen 1000 + link/ether 28:41:c6:xx:xx:aa brd ff:ff:ff:ff:ff:ff + altname enx2841c6xxxxaa + inet 192.168.9.1/24 scope global enp1s0f0 + valid_lft forever preferred_lft forever + inet6 fe80::2a41:c6ff:fexx:xxaa/64 scope link proto kernel_ll + valid_lft forever preferred_lft forever + +sudo ip netns exec netns_eth1 ifconfig enp1s0f1 192.168.9.2 netmask 255.255.255.0 + +sudo ip netns exec netns_eth1 ip a +1: lo: mtu 65536 qdisc noop state DOWN group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 +25: enp1s0f1: mtu 1500 qdisc mq state UP group default qlen 1000 + link/ether 28:41:c6:xx:xx:ab brd ff:ff:ff:ff:ff:ff + altname enx2841c6xxxxab + inet 192.168.9.2/24 scope global enp1s0f1 + valid_lft forever preferred_lft forever + inet6 fe80::2a41:c6ff:fexx:xxab/64 scope link proto kernel_ll + valid_lft forever preferred_lft forever +``` +Test is using ping. On both switches one of the 2.5Gbit links and all 10GBit links should show activity: +``` +$ sudo ip netns exec netns_eth1 ping 192.168.9.1 +PING 192.168.9.1 (192.168.9.1) 56(84) bytes of data. +64 bytes from 192.168.9.1: icmp_seq=1 ttl=64 time=0.082 ms +64 bytes from 192.168.9.1: icmp_seq=2 ttl=64 time=0.130 ms +^C +--- 192.168.9.1 ping statistics --- +2 packets transmitted, 2 received, 0% packet loss, time 1030ms +rtt min/avg/max/mdev = 0.082/0.106/0.130/0.024 ms +``` +You can also verify that the redundancy works by unplugging the active link, the ping should continue +undisturbed with the other link now tranporting the pakets. + +In 2 shells, start 2 instances of iperf, listening on 2 different ports. You will need to make sure that +the hash algorithm assigns different switch ports for the different port numbers. You can check this by +running the iperf3 client against each server instance and verify that different links show activity: +``` +sudo ip netns exec netns_eth0 iperf3 -s + +sudo ip netns exec netns_eth0 iperf3 -s -p 5333 +``` + +Now you can run the clients in parallel: +``` +$ sudo ip netns exec netns_eth1 iperf3 -c 192.168.9.1 & sudo ip netns exec netns_eth1 iperf3 -p 5333 -c 192.168.9.1 +[1] 295484 +Connecting to host 192.168.9.1, port 5201 +[ 5] local 192.168.9.2 port 60996 connected to 192.168.9.1 port 5201 +Connecting to host 192.168.9.1, port 5333 +[ 5] local 192.168.9.2 port 39660 connected to 192.168.9.1 port 5333 +[ ID] Interval Transfer Bitrate Retr Cwnd +[ 5] 0.00-1.00 sec 283 MBytes 2.37 Gbits/sec 485 272 KBytes +[ ID] Interval Transfer Bitrate Retr Cwnd +[ 5] 0.00-1.00 sec 283 MBytes 2.37 Gbits/sec 479 379 KBytes +[ 5] 1.00-2.00 sec 280 MBytes 2.35 Gbits/sec 444 260 KBytes +[ 5] 1.00-2.00 sec 280 MBytes 2.35 Gbits/sec 578 267 KBytes +[ 5] 2.00-3.00 sec 281 MBytes 2.36 Gbits/sec 385 263 KBytes +[ 5] 2.00-3.00 sec 280 MBytes 2.35 Gbits/sec 373 375 KBytes +[ 5] 3.00-4.00 sec 280 MBytes 2.35 Gbits/sec 430 385 KBytes +[ 5] 3.00-4.00 sec 280 MBytes 2.35 Gbits/sec 452 273 KBytes +[ 5] 4.00-5.00 sec 281 MBytes 2.36 Gbits/sec 319 256 KBytes +[ 5] 4.00-5.00 sec 281 MBytes 2.36 Gbits/sec 425 269 KBytes +[ 5] 5.00-6.00 sec 280 MBytes 2.35 Gbits/sec 364 264 KBytes +[ 5] 5.00-6.00 sec 281 MBytes 2.36 Gbits/sec 561 264 KBytes +[ 5] 6.00-7.00 sec 281 MBytes 2.35 Gbits/sec 446 255 KBytes +[ 5] 6.00-7.00 sec 280 MBytes 2.35 Gbits/sec 582 263 KBytes +[ 5] 7.00-8.00 sec 281 MBytes 2.35 Gbits/sec 494 263 KBytes +[ 5] 7.00-8.00 sec 280 MBytes 2.35 Gbits/sec 539 181 KBytes +[ 5] 8.00-9.00 sec 281 MBytes 2.36 Gbits/sec 617 389 KBytes +[ 5] 8.00-9.00 sec 280 MBytes 2.35 Gbits/sec 490 232 KBytes +[ 5] 9.00-10.00 sec 281 MBytes 2.35 Gbits/sec 363 215 KBytes +- - - - - - - - - - - - - - - - - - - - - - - - - +[ ID] Interval Transfer Bitrate Retr +[ 5] 0.00-10.00 sec 2.74 GBytes 2.36 Gbits/sec 4347 sender +[ 5] 0.00-10.00 sec 2.74 GBytes 2.35 Gbits/sec receiver + +iperf Done. +[ 5] 9.00-10.00 sec 282 MBytes 2.36 Gbits/sec 536 380 KBytes +- - - - - - - - - - - - - - - - - - - - - - - - - +[ ID] Interval Transfer Bitrate Retr +[ 5] 0.00-10.00 sec 2.74 GBytes 2.36 Gbits/sec 5015 sender +[ 5] 0.00-10.00 sec 2.74 GBytes 2.35 Gbits/sec receiver + +iperf Done. +[1]+ Done sudo ip netns exec netns_eth1 iperf3 -c 192.168.9.1 +``` +As you can see, the total throughput was 4.71 GBit/sec which is close to the +maximum possible with a single 5GBit link. diff --git a/doc/trunking.md b/doc/trunking.md deleted file mode 100644 index 948f533..0000000 --- a/doc/trunking.md +++ /dev/null @@ -1,25 +0,0 @@ -# Trunking - -The RTL827x allows to combine multiple ports to a single logical port -(trunking). Up to 2 trunk groups can be defined. - -## Trunking control -Two registers RTL837x_TRUNK_CTRL_A (0x4f38) and RTL837x_TRUNK_CTRL_B (0x4f3c) -define the trunk groups. Each holds a bitmap of ports making up the trunk -group. - -## Trunking API -The code currently provides the following functions: -``` -void trunk_set(uint8_t group, uint16_t mask) __banked -``` - -# VLAN configuration on the Serial Console -For testing the following commands are provided on the serial console: -``` -trunk [p1] [p2]... - create or set a trunk group. Trunk-ID is 1 or 2. ports a physical ports - -trunk d - deletes the trunk group -``` diff --git a/html/lag.html b/html/lag.html new file mode 100644 index 0000000..85c6dff --- /dev/null +++ b/html/lag.html @@ -0,0 +1,28 @@ + + + + + + Link Aggregation Configuration + + + +
+
+

Link Aggregation Groups Configuration

+

LAG 1

+
+
+

LAG 2

+
+
+

LAG 3

+
+
+

LAG 4

+
+ +
+ + + diff --git a/html/lag.js b/html/lag.js new file mode 100644 index 0000000..4006880 --- /dev/null +++ b/html/lag.js @@ -0,0 +1,78 @@ +var lagInterval = Number(); + +function lagForm() { + if (!numPorts) + return; + clearInterval(lagInterval); + for (let j=0; j < 4; j++) { + var lag = "mLAG" + j + console.log("Adding LAG " + lag) + var m = document.getElementById(lag); + for (let i = 1; i <= numPorts; i++) { + const d = document.createElement("div"); + d.classList.add("cbgroup"); + const l = document.createElement("label"); + l.innerHTML = "" + i; + l.classList.add("cbgroup"); + const inp = document.createElement("input"); + inp.type = "checkbox"; inp.setAttribute("class","psel"); + inp.id = "p_" + lag + "_" + i; + const o = document.createElement("img"); + if (pIsSFP[i - 1]) { + o.src = "sfp.svg"; o.width ="60"; o.height ="60"; + } else { + o.src = "port.svg"; o.width = "40"; o.height = "40"; + } + l.appendChild(inp); l.appendChild(o); + d.appendChild(l) + m.appendChild(d); + } + } + fetchLag(); +} + +function setL(p, c){ + console.log("LAG setting: ", p, " to ", c); + document.getElementById(p).checked=c; +} +window.addEventListener("load", function() { + lagInterval = setInterval(lagForm, 200); +}); + +function fetchLag() { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + const s = JSON.parse(xhttp.responseText); + console.log("LAG: ", JSON.stringify(s)); + for (let l = 0; l < 4; l++) { + let members = parseInt(s[l].members, 2); + let hash = parseInt(s[l].hash, 16); + for (let i = 1; i <= numPorts; i++) { + let p = i - 1; + if (numPorts < 9) + p = physToLogPort[p]; + setL("p_mLAG"+l+"_"+i, members & (1<

Mirror Configuration

-
+

Mirrored Ports (TX)

diff --git a/html/mirror.js b/html/mirror.js index b536405..b66df15 100644 --- a/html/mirror.js +++ b/html/mirror.js @@ -49,8 +49,10 @@ function fetchMirror() { let m_tx = parseInt(s.mirror_tx, 2); let m_rx = parseInt(s.mirror_rx, 2); for (let i = 1; i <= numPorts; i++) { - setM("mPortsTX"+i, m_tx&1); setM("mPortsRX"+i, m_rx&1); - m_tx = m_tx >> 1; m_rx = m_tx >> 1; + let p = i - 1; + if (numPorts < 9) + p = physToLogPort[p];members & (1<Port Statistics" + "
  • VLAN
  • " + "
  • Mirroring
  • " - + "
  • Port Aggregation
  • " + + "
  • Link Aggregation
  • " + "
  • EEE
  • " + "
  • System Settings
  • " + "
  • Firmware Update
  • "; diff --git a/html/ports.js b/html/ports.js index a8f0392..0a2fe2a 100644 --- a/html/ports.js +++ b/html/ports.js @@ -1,3 +1,6 @@ +const logToPhysPort = [0, 0, 0, 5, 1, 2, 3, 4, 6]; +const physToLogPort = [ 4, 5, 6, 7, 3, 8]; + function drawPorts() { var f = document.getElementById('ports'); console.log("DRAWING PORTS: ", numPorts); diff --git a/httpd/httpd.c b/httpd/httpd.c index eb25cfd..8f6c923 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -570,6 +570,8 @@ void httpd_appcall(void) send_eee(); } else if (is_word(q, "/mirror.json")) { send_mirror(); + } else if (is_word(q, "/lag.json")) { + send_lag(); } else if (is_word(q, "/config")) { send_config(); } else if (is_word(q, "/cmd_log")) { diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 5c66f99..21c4b38 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -216,9 +216,8 @@ void send_counters(char port) void send_mirror(void) { - print_string("send_eee called\n"); + print_string("send_mirror called\n"); slen = strtox(outbuf, HTTP_RESPONCE_JSON); - print_string("sending EEE status\n"); reg_read_m(RTL837x_MIRROR_CTRL); uint8_t mPort = sfr_data[3]; @@ -237,14 +236,14 @@ void send_mirror(void) m = (m << 8) | sfr_data[1]; slen += strtox(outbuf + slen, ",\"mirror_rx\":\""); for (uint8_t i = 0; i < 16; i++) { - bool_to_html(m & 0x8000); + bool_to_html(!!(m & 0x8000)); m <<= 1; } m = sfr_data[2]; m = (m << 8) | sfr_data[3]; slen += strtox(outbuf + slen, "\",\"mirror_tx\":\""); for (uint8_t i = 0; i < 16; i++) { - bool_to_html(m & 0x8000); + bool_to_html(!!(m & 0x8000)); m <<= 1; } char_to_html('\"'); @@ -252,6 +251,32 @@ void send_mirror(void) } +void send_lag(void) +{ + print_string("send_lag called\n"); + slen = strtox(outbuf, HTTP_RESPONCE_JSON); + + char_to_html('['); + for (uint8_t l=0; l < 4; l++) { + slen += strtox(outbuf + slen, "{\"lagNum\":"); + itoa_html(l); + slen += strtox(outbuf + slen, ",\"members\":\""); + reg_read_m(RTL837X_TRK_MBR_CTRL_BASE + (l << 2)); + uint16_t ports = ((uint16_t)sfr_data[2] << 8) | sfr_data[3]; + for (uint8_t i = 0; i < 16; i++) { + bool_to_html(!!(ports & 0x8000)); + ports <<= 1; + } + slen += strtox(outbuf + slen, "\",\"hash\":\""); + reg_read_m(RTL837X_TRK_HASH_CTRL_BASE + (l << 2)); + sfr_data_to_html(); + slen += strtox(outbuf + slen, "\"},"); + } + slen -=1; // remove comma + char_to_html(']'); +} + + void send_eee(void) { print_string("send_eee called\nsending EEE status\n"); diff --git a/httpd/page_impl.h b/httpd/page_impl.h index 709cbd3..166a79c 100644 --- a/httpd/page_impl.h +++ b/httpd/page_impl.h @@ -9,6 +9,7 @@ void send_eee(void); void send_mirror(void); void send_config(void); void send_cmd_log(void); +void send_lag(void); /* Convert only the lower nibble to ascii HEX char. For convenience the upper nibble is masked out. diff --git a/rtl837x_port.c b/rtl837x_port.c index b181cc6..26c5dd1 100644 --- a/rtl837x_port.c +++ b/rtl837x_port.c @@ -212,10 +212,10 @@ void vlan_setup(void) __banked write_char(' '); write_char('A'); write_char('>'); print_sfr_data(); #endif - // EGRESS filtering for port: removal of additional VLAN tag - reg_bit_clear(0x6738, i << 1); - reg_bit_clear(0x6738, (i << 1) + 1); - reg_bit_set(0x4e18, i); + // EGRESS filtering for port: removal of additional VLAN tag (mode 0x3 for each port) + reg_bit_clear(RTL837X_VLAN_PORT_EGR_TAG, i << 1); + reg_bit_clear(RTL837X_VLAN_PORT_EGR_TAG, (i << 1) + 1); + reg_bit_set(RTL837X_VLAN_PORT_IGR_FLTR, i); #ifdef DEBUG print_string("\n"); @@ -226,9 +226,9 @@ void vlan_setup(void) __banked REG_SET(RTL837x_REG_INGRESS, 0); // No filtering for all ports // Enable 4k VLAN - REG_SET(0x4e14, 4); - REG_SET(0x4e30, 0); - REG_SET(0x4e34, 0); + REG_SET(RTL837X_VLAN_CTRL, VLAN_CVLAN_FILTER); + REG_SET(RTL837X_VLAN_L2_LRN_DIS_0, 0); + REG_SET(RTL837X_VLAN_L2_LRN_DIS_1, 0); // Enable VLAN 1: Ports 0-9, i.e. including the CPU port are untagged members if (isRTL8373) { @@ -241,12 +241,6 @@ void vlan_setup(void) __banked reg_read_m(RTL837X_TBL_CTRL); } while (sfr_data[3] & TBL_EXECUTE); - // Configure trunking - if (isRTL8373) { - REG_SET(0x4f4c, 0x0000007e); // Removes RTL VLAN-Tags - REG_SET(0x4f48, 0x0000007e); // Adds 802.1Q VLAN-Tags to tagged ports - } - #ifdef DEBUG print_string("\nvlan_setup, REG 0x6738: "); print_reg(0x6738); print_string("\nvlan_setup, REG 0x4e18: "); print_reg(0x4e18); @@ -261,18 +255,6 @@ void vlan_setup(void) __banked } -void trunk_set(uint8_t group, uint16_t mask) __banked -{ - if (group == 1) { - REG_WRITE(RTL837x_TRUNK_CTRL_A, 0, 0, mask >> 8, mask); - } else if (group == 2) { - REG_WRITE(RTL837x_TRUNK_CTRL_B, 0, 0, mask >> 8, mask); - } else { - print_string("\nTrunk group must be 1 or 2\n"); - } -} - - /* * Forget all dynamic L2 learned entries */ @@ -377,7 +359,8 @@ void port_l2_setup(void) __banked port_l2_forget(); for (uint8_t i = minPort; i <= maxPort; i++) { - uint16_t reg = 0x5384 + (i << 2); + // Limit the number of automatically learned MAC-Entries per port to 0x1040 + uint16_t reg = RTL837X_L2_LRN_PORT_CONSTRAINT + (i << 2); REG_SET(reg, 0x00001040); // All ports may communicate with each other and CPU-Port @@ -388,7 +371,8 @@ void port_l2_setup(void) __banked REG_SET(reg, PMASK_6 | PMASK_CPU); } } - reg_bit_set(0x4f80, 0); + // When maximim entries learned, then simply flood the packet + reg_bit_set(RTL837X_L2_LRN_PORT_CONSTRT_ACT, 0); print_string("\nport_l2_setup done\n"); } @@ -597,3 +581,35 @@ void port_rldp_on(__xdata uint16_t p_ms) REG_SET(RTL837X_RMA0_CONF, 0x00000000); // R4ecc REG_SET(RTL837X_RMA_CONF, 0x00000000); // R4ecc } + + +/* + * Configure LAGs + * Sets the members via port bitmask of a given Link Aggregation Group + * The groups have numbers 0-3 + * The bitmask represents up to 10 ports + * If currently no LAG has algorithm used, a default is applied + */ +void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banked +{ + print_string("port_lag_members_set, lag: "); print_byte(lag); print_string(", members: "); print_short(members); + if (lag > 3) + print_string("Link aggregation group must be 0-3!"); + reg_read_m(RTL837X_TRK_HASH_CTRL_BASE + (lag << 2)); + if (!(sfr_data[0] | sfr_data [1] | sfr_data [2] | sfr_data [3])) + REG_SET(RTL837X_TRK_HASH_CTRL_BASE, LAG_HASH_DEFAULT); + REG_WRITE(RTL837X_TRK_MBR_CTRL_BASE + (lag << 2), 0, 0, members >> 8, members & 0xff); +} + + +/* + * Configures the hash algorithm used for a LAG + * lag is the Group to configure and hash is a bitmask + */ +void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash_bits) __banked +{ + print_string("port_lag_hash_set, lag: "); print_byte(lag); print_string(", hash: "); print_byte(hash_bits); + if (lag > 3) + print_string("Link aggregation group must be 0-3!"); + REG_WRITE(RTL837X_TRK_HASH_CTRL_BASE + (lag << 2), 0, 0, 0, hash_bits); +} diff --git a/rtl837x_port.h b/rtl837x_port.h index 4b5f39b..e551c11 100644 --- a/rtl837x_port.h +++ b/rtl837x_port.h @@ -26,7 +26,8 @@ void port_mirror_set(register uint8_t port, __xdata uint16_t rx_pmask, __xdata u void port_mirror_del(void) __banked; void port_ingress_filter(register uint8_t port, uint8_t type) __banked; void port_l2_setup(void) __banked; -void trunk_set(uint8_t group, uint16_t mask) __banked; +void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banked; +void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash) __banked; void port_eee_enable_all(void) __banked; void port_eee_disable_all(void) __banked; void port_eee_status_all(void) __banked; diff --git a/rtl837x_regs.h b/rtl837x_regs.h index ce6052e..fb4b6b9 100644 --- a/rtl837x_regs.h +++ b/rtl837x_regs.h @@ -117,6 +117,17 @@ #define RTL837x_L2_TBL_FLUSH_CTRL 0x53d4 #define L2_TBL_FLUSH_EXEC 0x10000 #define RTL837x_L2_TBL_FLUSH_CNF 0x53dc +#define RTL837X_L2_LRN_PORT_CONSTRAINT 0x5384 +#define RTL837X_L2_LRN_PORT_CONSTRT_ACT 0x4f80 +/* + * VLAN configuration + */ +#define RTL837X_VLAN_CTRL 0x4e14 +#define VLAN_CVLAN_FILTER 0x4 +#define RTL837X_VLAN_PORT_EGR_TAG 0x6738 +#define RTL837X_VLAN_PORT_IGR_FLTR 0x4e18 +#define RTL837X_VLAN_L2_LRN_DIS_0 0x4e30 +#define RTL837X_VLAN_L2_LRN_DIS_1 0x4e34 /* * Egress / ingress filtering @@ -134,10 +145,18 @@ #define RTL837x_MIRROR_CTRL 0x6048 /* - * Trunking + * Link Aggregation aka Trunking */ -#define RTL837x_TRUNK_CTRL_A 0x4f38 -#define RTL837x_TRUNK_CTRL_B 0x4f3c +#define RTL837X_TRK_MBR_CTRL_BASE 0x4f38 +#define RTL837X_TRK_HASH_CTRL_BASE 0x4f48 +#define LAG_HASH_SOURCE_PORT_NUMBER 0x01 +#define LAG_HASH_L2_SMAC 0x02 +#define LAG_HASH_L2_DMAC 0x04 +#define LAG_HASH_L3_SIP 0x08 +#define LAG_HASH_L3_DIP 0x10 +#define LAG_HASH_L4_SPORT 0x20 +#define LAG_HASH_L4_DPORT 0x40 +#define LAG_HASH_DEFAULT (LAG_HASH_L2_SMAC | LAG_HASH_L2_DMAC | LAG_HASH_L3_SIP | LAG_HASH_L3_DIP | LAG_HASH_L4_SPORT | LAG_HASH_L4_DPORT) /* * Port isolation diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c index 8d45d19..3463d23 100644 --- a/tools/httpd_sim.c +++ b/tools/httpd_sim.c @@ -13,7 +13,7 @@ #define SESSION_ID "1234567890ab" #define PASSWORD "1234" -#define SESSION_TIMEOUT 20 +#define SESSION_TIMEOUT 2000 #define PORTS 6 time_t last_called; @@ -216,6 +216,31 @@ void send_mirror(int s) } +void send_lag(int s) +{ + char lag_buf[20]; + struct json_object *lags = json_object_new_array_ext(4); + struct json_object *v; + const char *jstring; + char *header = "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json; charset=UTF-8\r\n\r\n"; + + for (int i = 0; i < 4; i++) { + v = json_object_new_object(); + json_object_object_add(v, "lagNum", json_object_new_int(i)); + sprintf(lag_buf, "%016b", 0x30 << (i * 2) & 0x1f8); + json_object_object_add(v, "members", json_object_new_string(lag_buf)); + json_object_object_add(v, "hash", json_object_new_string("0x7e")); + json_object_array_add(lags, v); + } + write(s, header, strlen(header)); + + jstring = json_object_to_json_string_ext(lags, JSON_C_TO_STRING_PLAIN); + write(s, jstring, strlen(jstring)); + json_object_put(lags); +} + + void send_cmd_log(int s) { char *header = "HTTP/1.1 200 OK\r\n" @@ -417,6 +442,13 @@ void launch(struct Server *server) else send_mirror(new_socket); goto done; + } else if (!strncmp(&buffer[4], "/lag.json", 9)) { + printf("LAG request\n"); + if (!authenticated) + send_unauthorized(new_socket); + else + send_lag(new_socket); + goto done; } else if (!strncmp(&buffer[4], "/vlan.json?vid=", 15)) { int vlan = atoi(&buffer[19]); printf("VLAN request for %d\n", vlan);