diff options
author | Ben Hilburn <ben.hilburn@ettus.com> | 2014-02-04 11:04:07 -0800 |
---|---|---|
committer | Ben Hilburn <ben.hilburn@ettus.com> | 2014-02-04 11:04:07 -0800 |
commit | 178ac3f1c9950d383c8f64b3df464c0f943c4a23 (patch) | |
tree | 318ed621a7b59b7d34d4ce6e4a92f73f0bcef509 /firmware/x300/lib/link_state_route_proto.c | |
parent | 2718ac110fa931cc29daf7cb3dc5ab6230ee02ab (diff) | |
download | uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.tar.gz uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.tar.bz2 uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.zip |
Merging USRP X300 and X310 support!!
Diffstat (limited to 'firmware/x300/lib/link_state_route_proto.c')
-rw-r--r-- | firmware/x300/lib/link_state_route_proto.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/firmware/x300/lib/link_state_route_proto.c b/firmware/x300/lib/link_state_route_proto.c new file mode 100644 index 000000000..30cfd73cb --- /dev/null +++ b/firmware/x300/lib/link_state_route_proto.c @@ -0,0 +1,441 @@ + +// Copyright 2013 Ettus Research LLC + +#include <link_state_route_proto.h> +#include <u3_net_stack.h> +#include <ethernet.h> +#include <string.h> +#include <printf.h> +#include <print_addrs.h> + +#define lengthof(a) (sizeof(a)/sizeof(*(a))) + +/*********************************************************************** + * global constants + **********************************************************************/ +#define LS_PROTO_VERSION 6 + +//shift the proto version into the ID so only matching fw responds +#define LS_ID_DISCOVER (0 | (8 << LS_PROTO_VERSION)) +#define LS_ID_INFORM (1 | (8 << LS_PROTO_VERSION)) + +#define LS_PAYLOAD_MTU 1024 +#define LS_NUM_NBOR_ENTRIES 16 +#define LS_NUM_NODE_ENTRIES 64 +#define LS_NUM_MAP_ENTRIES 128 + +#define NETHS 4 //max eths supported in this file + +/*********************************************************************** + * wire format for table communication + **********************************************************************/ +typedef struct +{ + uint32_t num_nbors; //number of entries in neighbors list + uint32_t num_ports; //first few neighbors are local ports + struct ip_addr node; + struct ip_addr nbors[]; +} ls_data_t; + +static inline size_t sizeof_ls_data(const ls_data_t *ls_data) +{ + return 0 + + sizeof(uint32_t)/*num neighbors*/ + + sizeof(uint32_t)/*num ports*/ + + sizeof(struct ip_addr)/*source node*/ + + sizeof(struct ip_addr)*ls_data->num_nbors; +} + +/*********************************************************************** + * sequence and tick counter monitor + **********************************************************************/ +static uint16_t ticker = 0; + +void link_state_route_proto_tick(void) +{ + ticker++; +} + +static inline bool is_tick_expired(const uint16_t tick) +{ + const uint16_t delta = ticker - tick; + return delta > 2; //have not talked in a while, you are deaf to me +} + +static uint16_t current_seq = 0; + +static inline bool is_seq_newer(const uint16_t seq, const uint16_t entry_seq) +{ + if (seq == entry_seq) return false; //not newer if equal + const uint16_t delta = seq - entry_seq; + return (delta & (1 << 15)) == 0; //newer when subtraction did not overflow +} + +/*********************************************************************** + * node entry api + **********************************************************************/ +typedef struct +{ + uint16_t seq; + uint16_t tick; + uint8_t ethno; + struct ip_addr ip_addr; +} ls_node_entry_t; + +static bool ls_node_entry_valid(const ls_node_entry_t *entry) +{ + return entry->ip_addr.addr != 0 && !is_tick_expired(entry->tick); +} + +static void ls_node_entry_update(ls_node_entry_t *entry, const int8_t ethno, const uint16_t seq, const struct ip_addr *ip_addr) +{ + entry->seq = seq; + entry->tick = ticker; + entry->ethno = ethno; + entry->ip_addr.addr = ip_addr->addr; +} + +static bool ls_node_entries_update( + ls_node_entry_t *entries, const size_t num_entries, + const int8_t ethno, const uint16_t seq, const struct ip_addr *ip_addr +) +{ + for (size_t i = 0; i < num_entries; i++) + { + if (!ls_node_entry_valid(&entries[i])) + { + ls_node_entry_update(entries+i, ethno, seq, ip_addr); + return true; + } + + if (entries[i].ip_addr.addr == ip_addr->addr && entries[i].ethno == ethno) + { + if (is_seq_newer(seq, entries[i].seq)) + { + ls_node_entry_update(entries+i, ethno, seq, ip_addr); + return true; + } + return false; + } + } + + //no space, shift the table down and take entry 0 + memmove(entries+1, entries, (num_entries-1)*sizeof(ls_node_entry_t)); + ls_node_entry_update(entries+0, ethno, seq, ip_addr); + return true; +} + +/*********************************************************************** + * storage for nodes in the network + **********************************************************************/ +static ls_node_entry_t ls_nbors[LS_NUM_NBOR_ENTRIES]; +static ls_node_entry_t ls_nodes[LS_NUM_NODE_ENTRIES]; + +/*********************************************************************** + * node table + **********************************************************************/ +static ls_node_mapping_t ls_node_maps[LS_NUM_MAP_ENTRIES]; + +const ls_node_mapping_t *link_state_route_get_node_mapping(size_t *length) +{ + *length = lengthof(ls_node_maps); + return ls_node_maps; +} + +static void add_node_mapping(const struct ip_addr *node, const struct ip_addr *nbor) +{ + //printf("add_node_mapping: %s -> %s\n", ip_addr_to_str(node), ip_addr_to_str(nbor)); + + //write into the first available slot + for (size_t i = 0; i < lengthof(ls_node_maps); i++) + { + if (ls_node_maps[i].node.addr == 0) + { + ls_node_maps[i].node.addr = node->addr; + ls_node_maps[i].nbor.addr = nbor->addr; + return; + } + } + + //otherwise, shift down the table and take slot0 + memmove(ls_node_maps+1, ls_node_maps, sizeof(ls_node_maps) - sizeof(ls_node_mapping_t)); + ls_node_maps[0].node.addr = node->addr; + ls_node_maps[0].nbor.addr = nbor->addr; +} + +static void remove_node_matches(const struct ip_addr *node) +{ + //printf("remove_node_matches: %s\n", ip_addr_to_str(node)); + + for (size_t j = 0; j < lengthof(ls_node_maps); j++) + { + //if the address is a match, clear the entry + if (ls_node_maps[j].node.addr == node->addr) + { + ls_node_maps[j].node.addr = 0; + ls_node_maps[j].nbor.addr = 0; + } + } +} + +static void update_node_mappings(const ls_data_t *ls_data) +{ + //printf("update_node_mappings: %s\n", ip_addr_to_str(&ls_data->node)); + + //remove any expired entries + for (size_t i = 0; i < lengthof(ls_nodes); i++) + { + if (ls_nodes[i].ip_addr.addr != 0 && is_tick_expired(ls_nodes[i].tick)) + { + remove_node_matches(&ls_nodes[i].ip_addr); + } + } + + //remove any matches for the current node + remove_node_matches(&ls_data->node); + + //is this a local packet? + bool is_local = false; + for (size_t e = 0; e < ethernet_ninterfaces(); e++) + { + if (ls_data->node.addr == u3_net_stack_get_ip_addr(e)->addr) is_local = true; + } + + //load entries from ls data into array + for (size_t i = 0; i < ls_data->num_nbors; i++) + { + if (is_local && i < ls_data->num_ports) continue; //ignore local ports + add_node_mapping(&ls_data->node, &ls_data->nbors[i]); + } +} + +/*********************************************************************** + * forward link state data onto all neighbors on the given port + **********************************************************************/ +static void send_link_state_data_to_all_neighbors( + const uint8_t ethno, const uint16_t seq, const ls_data_t *ls_data +){ + //exit and dont forward if the information is stale + if (!ls_node_entries_update(ls_nodes, lengthof(ls_nodes), ethno, seq, &ls_data->node)) return; + + //update the mappings with new info + update_node_mappings(ls_data); + + //forward to all neighbors + for (size_t i = 0; i < lengthof(ls_nbors); i++) + { + if (ls_nbors[i].ip_addr.addr == ls_data->node.addr) continue; //dont forward to sender + if (ls_node_entry_valid(&ls_nbors[i])) + { + if (ethernet_get_link_up(ls_nbors[i].ethno)) u3_net_stack_send_icmp_pkt( + ls_nbors[i].ethno, ICMP_IRQ, 0, + LS_ID_INFORM, seq, + &(ls_nbors[i].ip_addr), ls_data, sizeof_ls_data(ls_data) + ); + } + } + + //a change may have occured, update the cache + link_state_route_proto_update_cycle_cache(ethno); +} + +/*********************************************************************** + * handler for information reply + **********************************************************************/ +static void handle_icmp_ir( + const uint8_t ethno, + const struct ip_addr *src, const struct ip_addr *dst, + const uint16_t id, const uint16_t seq, + const void *buff, const size_t num_bytes +){ + switch (id) + { + //received a reply directly from the neighbor, add to neighbor list + case LS_ID_DISCOVER: + //printf("GOT LS_ID_DISCOVER REPLY - ID 0x%x - IP%u: %s\n", id, (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno))); + if (ls_node_entries_update(ls_nbors, lengthof(ls_nbors), ethno, seq, src)) link_state_route_proto_flood(ethno); + break; + } +} + +/*********************************************************************** + * handler for information request + **********************************************************************/ +static void handle_icmp_irq( + const uint8_t ethno, + const struct ip_addr *src, const struct ip_addr *dst, + const uint16_t id, const uint16_t seq, + const void *buff, const size_t num_bytes +){ + switch (id) + { + //replies to discovery packets + case LS_ID_DISCOVER: + //printf("GOT LS_ID_DISCOVER REQ - IP%u: %s\n", (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno))); + //printf("SEND LS_ID_DISCOVER REPLY - IP%u: %s\n", (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno))); + u3_net_stack_send_icmp_pkt(ethno, ICMP_IR, 0, id, seq, src, buff, num_bytes); + break; + + //handle and forward information + case LS_ID_INFORM: + //printf("GOT LS_ID_INFORM REQ - IP%u: %s\n", (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno))); + send_link_state_data_to_all_neighbors(ethno, seq, (const ls_data_t *)buff); + break; + }; +} + +/*********************************************************************** + * initiate a periodic update to the table + **********************************************************************/ +void link_state_route_proto_update(const uint8_t ethno) +{ + //send a discovery packet + //printf("SEND LS_ID_DISCOVER REQ - IP%u: %s\n", (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno))); + u3_net_stack_send_icmp_pkt( + ethno, ICMP_IRQ, 0, + LS_ID_DISCOVER, current_seq++, + u3_net_stack_get_bcast(ethno), NULL, 0 + ); +} + +void link_state_route_proto_flood(const uint8_t ethno) +{ + for (size_t e = 0; e < ethernet_ninterfaces(); e++) + { + //fill link state data buffer + uint8_t buff[LS_PAYLOAD_MTU] = {}; + ls_data_t *ls_data = (ls_data_t *)buff; + ls_data->node.addr = u3_net_stack_get_ip_addr(e)->addr; + ls_data->num_nbors = 0; + ls_data->num_ports = 0; + + //first the local port links + for (size_t ej = 0; ej < ethernet_ninterfaces(); ej++) + { + if (e == ej) continue; //dont include our own port + ls_data->nbors[ls_data->num_nbors++].addr = u3_net_stack_get_ip_addr(ej)->addr; + ls_data->num_ports++; + } + + //now list the neighbors + for (size_t i = 0; i < lengthof(ls_nbors); i++) + { + if ((sizeof_ls_data(ls_data) + 4) >= LS_PAYLOAD_MTU) break; + if (ls_node_entry_valid(&ls_nbors[i]) && ls_nbors[i].ethno == e) + { + ls_data->nbors[ls_data->num_nbors++].addr = ls_nbors[i].ip_addr.addr; + } + } + + //send this data to all neighbors + send_link_state_data_to_all_neighbors(ethno, current_seq++, ls_data); + } +} + +/*********************************************************************** + * cycle detection logic + **********************************************************************/ +static void follow_links(const size_t current, struct ip_addr *nodes, bool *visited, const size_t num_nodes) +{ + if (visited[current]) return; //end the recursion + visited[current] = true; + + //follow all links where current node is the source + for (size_t i = 0; i < lengthof(ls_node_maps); i++) + { + if (ls_node_maps[i].node.addr != nodes[current].addr) continue; + + //find the index of the neighbor in the node list to recurse + for (size_t j = 0; j < num_nodes; j++) + { + if (nodes[j].addr != ls_node_maps[i].nbor.addr) continue; + follow_links(j, nodes, visited, num_nodes); + } + } +} + +bool link_state_route_proto_causes_cycle(const struct ip_addr *src, const struct ip_addr *dst) +{ + //printf("is there a cycle? %s -> %s: \n", ip_addr_to_str(src), ip_addr_to_str(dst)); + + //make a set of all nodes + size_t num_nodes = 0; + struct ip_addr nodes[LS_NUM_MAP_ENTRIES]; + for (size_t i = 0; i < lengthof(ls_node_maps); i++) + { + if (ls_node_maps[i].node.addr == 0 || ls_node_maps[i].nbor.addr == 0) continue; + //printf(" Link %s -> %s\n", ip_addr_to_str(&ls_node_maps[i].node), ip_addr_to_str(&ls_node_maps[i].nbor)); + const struct ip_addr *node = &ls_node_maps[i].node; + + //check if we have an entry + for (size_t j = 0; j < num_nodes; j++) + { + if (nodes[j].addr == node->addr) goto skip_add; + } + + //otherwise, we add the node + nodes[num_nodes++].addr = node->addr; + //printf(" Add to node set: %s\n", ip_addr_to_str(node)); + skip_add: continue; + } + + //and stateful tracking info for each node + bool visited[LS_NUM_MAP_ENTRIES]; + for (size_t i = 0; i < num_nodes; i++) visited[i] = false; + + //find our src node in the set and follow + for (size_t i = 0; i < num_nodes; i++) + { + if (nodes[i].addr == src->addr) follow_links(i, nodes, visited, num_nodes); + } + + //did we visit the destination? if so, there is a cycle + for (size_t i = 0; i < num_nodes; i++) + { + if (nodes[i].addr == dst->addr && visited[i]) + { + //printf("CAUSES CYCLE!\n"); + return true; + } + } + + //printf("no cycle found.\n"); + return false; +} + +static bool ls_causes_cycle[NETHS][NETHS]; + +void link_state_route_proto_update_cycle_cache(const uint8_t eth_src) +{ + for (size_t eth_dst = 0; eth_dst < ethernet_ninterfaces(); eth_dst++) + { + if (eth_src == eth_dst) continue; + ls_causes_cycle[eth_src][eth_dst] = link_state_route_proto_causes_cycle( + u3_net_stack_get_ip_addr(eth_src), + u3_net_stack_get_ip_addr(eth_dst) + ); + } +} + +bool link_state_route_proto_causes_cycle_cached(const uint8_t eth_src, const uint8_t eth_dst) +{ + return ls_causes_cycle[eth_src][eth_dst]; +} + +/*********************************************************************** + * init and registration code + **********************************************************************/ +void link_state_route_proto_init(void) +{ + u3_net_stack_register_icmp_handler(ICMP_IRQ, 0, &handle_icmp_irq); + u3_net_stack_register_icmp_handler(ICMP_IR, 0, &handle_icmp_ir); + + //default to causing a cycle, let the algorithm set this correctly + for (size_t i = 0; i < NETHS; i++) + { + for (size_t j = 0; j < NETHS; j++) + { + ls_causes_cycle[i][j] = true; + } + } +} |