From 9388610da20356169523abd03af1a4e569a9f91e Mon Sep 17 00:00:00 2001 From: Mauricio Vasquez B Date: Wed, 1 Mar 2017 23:29:10 -0500 Subject: [PATCH] iomodules/router: refactor router code - use direct packet access in the router - use standard datatypes for saving ip, macs, netmasks and networks - use function from iomodules/utils.go to parse data --- iomodules/router/router.go | 598 ++++++++++++++++------------------ iomodules/router/routerAPI.go | 173 ++++------ iomodules/utils.go | 5 + mainlogic/mainlogic.go | 16 +- mainlogic/print.go | 6 +- 5 files changed, 365 insertions(+), 433 deletions(-) diff --git a/iomodules/router/router.go b/iomodules/router/router.go index 72df2bb..1ef246e 100644 --- a/iomodules/router/router.go +++ b/iomodules/router/router.go @@ -13,76 +13,67 @@ // limitations under the License. package router -//Sanity Check Packet -> minimum length and correct checksum -//decrement TTL and recompute packet checksum (l3 recompute checksum) - -//lookup in the longest prefix matching table: -//destination ip address of the packet. - -//LONGEST PREFIX MATCHING trivialimplementation - var RouterCode = ` #include #include -#undef BPF_TRACE -#define BPF_LOG -#undef BPF_TRACE_ICMP_ECHO_REPLY - -#undef CHECK_MAC_DST - -#define ROUTING_TABLE_DIM 8 -#define ROUTER_PORT_N 10 -#define ARP_TABLE_DIM 10 - -#define IP_TTL_OFFSET 8 -#define IP_CSUM_OFFSET 10 -#define IP_SRC_OFF 26 -#define IP_DST_OFF 30 -#define IP_CKSUM_OFF 24 -#define ICMP_CSUM_OFFSET (sizeof(struct ethernet_t) + sizeof(struct ip_t) + offsetof(struct icmp_hdr, checksum)) - -#define IP_ICMP 0x01 - -#define ICMP_ECHO_REQUEST 0x8 -#define ICMP_ECHO_REPLY 0x0 - -#define ETH_DST_OFFSET 0 -#define ETH_SRC_OFFSET 6 -#define ETH_TYPE_OFFSET 12 - -#define ETH_TYPE_IP 0x0800 -#define ETH_TYPE_ARP 0x0806 - -#define MAC_BROADCAST 0xffffffffffff -#define MAC_MULTICAST_MASK 0x010000000000 - -#define SLOWPATH_ARP_REPLY 1 -#define SLOWPATH_ARP_LOOKUP_MISS 2 -#define SLOWPATH_TTL_EXCEEDED 3 - -/*Only for ICMP echo req, reply*/ -struct icmp_hdr { - unsigned char type; - unsigned char code; - unsigned short checksum; - unsigned short id; - unsigned short seq; -} __attribute__((packed)); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The following flags control the debubbing output of the eBPF program. + * Please note that because of some eBPF limitations all of them cannot + * be activated at the same time + */ + +//#define BPF_TRACE // global trace control (should be comment to get better performance) + +#ifdef BPF_TRACE +//#define BPF_TRACE_TTL +//#define BPF_TRACE_INPUT +//#define BPF_TRACE_OUTPUT +//#define BPF_TRACE_ROUTING +//#define BPF_TRACE_ARP +//#define BPF_TRACE_ICMP_ECHO_REPLY +#endif + +//#define CHECK_MAC_DST + +#define ROUTING_TABLE_DIM 6 +#define ROUTER_PORT_N 32 +#define ARP_TABLE_DIM 32 + +#define IP_CSUM_OFFSET (sizeof(struct eth_hdr) + offsetof(struct iphdr, check)) +#define ICMP_CSUM_OFFSET (sizeof(struct eth_hdr) + sizeof(struct iphdr) + offsetof(struct icmphdr, checksum)) + +#define MAC_MULTICAST_MASK 0x10ULL // network byte order + +enum { + SLOWPATH_ARP_REPLY = 1, + SLOWPATH_ARP_LOOKUP_MISS, + SLOWPATH_TTL_EXCEEDED +}; /* Routing Table Entry */ struct rt_entry { - u32 network; //network: e.g. 192.168.1.0 - u32 netmask; //netmask: e.g. 255.255.255.0 - u32 port; //port of the router - u32 nexthop; //next hop: e.g. 192.168.1.254 (0 if local) + __be32 network; + __be32 netmask; + u16 port; + __be32 nexthop; // ip address of next hop, 0 is locally reachable }; /* Router Port */ struct r_port { - u32 ip; //ip addr : e.g. 192.168.1.254 - u32 netmask; //netmask : e.g. 255.255.255.0 - u64 mac; //mac addr: e.g. a1:b2:c3:ab:cd:ef + __be32 ip; + __be32 netmask; + __be64 mac:48; }; /* @@ -100,318 +91,281 @@ BPF_TABLE("array", u32, struct rt_entry, routing_table, ROUTING_TABLE_DIM); The mac address is used as mac_scr for the outcoming packet on that interface, and as mac address contained in the arp reply */ -BPF_TABLE("hash", u32, struct r_port, router_port, ROUTER_PORT_N); +BPF_TABLE("hash", u16, struct r_port, router_port, ROUTER_PORT_N); /* Arp Table implements a mapping between ip and mac addresses. */ BPF_TABLE("hash", u32, u64, arp_table, ARP_TABLE_DIM); -/* - Check multicast bit of a mac address. - If the address is broadcast is also multicast, so test multicast condition - is enough. -*/ -static inline bool is_multicast_or_broadcast(u64* mac) { - u64 mask = 0; - mask = *mac & MAC_MULTICAST_MASK; - if (mask == 0) - return false; - else - return true; -} +#define PRINT_MAC(x) (bpf_htonll(x)>>16) + +struct eth_hdr { + __be64 dst:48; + __be64 src:48; + __be16 proto; +} __attribute__((packed)); + +struct arp_hdr { + __be16 ar_hrd; /* format of hardware address */ + __be16 ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + __be16 ar_op; /* ARP opcode (command) */ + + __be64 ar_sha:48; /* sender hardware address */ + __be32 ar_sip; /* sender IP address */ + __be64 ar_tha:48; /* target hardware address */ + __be32 ar_tip; /* target IP address */ +} __attribute__((packed)); static int handle_rx(void *skb, struct metadata *md) { - u8 *cursor = 0; - struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); + struct __sk_buff *skb2 = (struct __sk_buff *)skb; + void *data = (void *)(long)skb2->data; + void *data_end = (void *)(long)skb2->data_end; + + struct eth_hdr *eth = data; + + if (data + sizeof(*eth) > data_end) + goto DROP; - #ifdef BPF_TRACE - bpf_trace_printk("[router-%d]: in_ifc:%d\n", md->module_id, md->in_ifc); - bpf_trace_printk("[router-%d]: eth_type:%x mac_scr:%lx mac_dst:%lx\n", - md->module_id, ethernet->type, ethernet->src, ethernet->dst); + #ifdef BPF_TRACE_INPUT + bpf_trace_printk("[router-%d]: in_ifc:%d\n", md->module_id, md->in_ifc); + //bpf_trace_printk("[router-%d]: eth_type:%x mac_scr:%lx mac_dst:%lx\n", + // md->module_id, bpf_htons(eth->proto), PRINT_MAC(eth->src), PRINT_MAC(eth->dst)); #endif + struct r_port *in_port = router_port.lookup(&md->in_ifc); + if (!in_port) { + #ifdef BPF_TRACE_INPUT + bpf_trace_printk("[router-%d]: received packet from non valid port : '%d'\n", + md->module_id, md->in_ifc); + #endif + goto DROP; + } + /* Check if the mac destination of the packet is multicast, broadcast, or the - unicast address of the router port. - If not, drop the packet. - Multicast addresses are managed as broadcast + unicast address of the router port. If not, drop the packet. */ #ifdef CHECK_MAC_DST - u64 ethdst = ethernet->dst; - if (!is_multicast_or_broadcast(ðdst)){ - struct r_port *r_port_p = 0; - r_port_p = router_port.lookup(&md->in_ifc); - if (r_port_p) { - if (r_port_p->mac != ethernet->dst) { - #ifdef BPF_LOG - bpf_trace_printk("[router-%d]: mac destination %lx MISMATCH %lx -> DROP packet.\n", - md->module_id, ethernet->dst, r_port_p->mac); - #endif - return RX_DROP; - } - } + if (eth->dst != in_port->mac && !(eth->dst & (__be64) MAC_MULTICAST_MASK)) { + #ifdef BPF_TRACE_INPUT + bpf_trace_printk("[router-%d]: mac destination %lx MISMATCH %lx\n", + md->module_id, PRINT_MAC(eth->dst), PRINT_MAC(in_port->mac)); + #endif + goto DROP; } #endif - switch (ethernet->type) { - case ETH_TYPE_IP: goto IP; //ipv4 packet - case ETH_TYPE_ARP: goto ARP; //arp packet + switch (eth->proto) { + case htons(ETH_P_IP): goto IP; // ipv4 packet + case htons(ETH_P_ARP): goto ARP; // arp packet + default: goto DROP; } - IP: ; //ipv4 packet - struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); +IP: ; // ipv4 packet + __be32 l3sum = 0; + struct iphdr *ip = data + sizeof(*eth); + if (data + sizeof(*eth) + sizeof(*ip) > data_end) + goto DROP; - #ifdef BPF_TRACE - bpf_trace_printk("[router-%d]: ttl:%u ip_scr:%x ip_dst:%x \n", md->module_id, ip->ttl, ip->src, ip->dst); - // bpf_trace_printk("[router-%d]: (before) ttl: %d checksum: %x\n", ip->ttl, ip->hchecksum); - #endif + #ifdef BPF_TRACE_TTL + bpf_trace_printk("[router-%d]: ttl: %u\n", md->module_id, ip->ttl); + #endif - /* ICMP Echo Responder for router ports */ - if ( ip->nextp == IP_ICMP) { - struct __sk_buff * skb2 = (struct __sk_buff *)skb; - void *data = (void *)(long)skb2->data; - void *data_end = (void *)(long)skb2->data_end; - struct icmp_hdr *icmp = data + sizeof(struct ethernet_t) + sizeof(struct ip_t); - - if (data + sizeof(struct ethernet_t) + sizeof(struct ip_t) + sizeof(*icmp) > data_end) - return RX_DROP; - - /*Only manage ICMP Request*/ - if ( icmp->type == ICMP_ECHO_REQUEST ){ - struct r_port *r_port_p = 0; - r_port_p = router_port.lookup(&md->in_ifc); - if (r_port_p) { - if (r_port_p->ip == ip->dst) { - //Reply to ICMP Echo request - - unsigned short type = ICMP_ECHO_REPLY; - bpf_l4_csum_replace(skb,36, icmp->type, type,sizeof(type)); - bpf_skb_store_bytes(skb, 34, &type, sizeof(type),0); - - unsigned int old_src = bpf_ntohl(ip->src); - unsigned int old_dst = bpf_ntohl(ip->dst); - bpf_l3_csum_replace(skb, IP_CKSUM_OFF, old_src, old_dst, sizeof(old_dst)); - - bpf_skb_store_bytes(skb, IP_SRC_OFF, &old_dst, sizeof(old_dst), 0); - bpf_l3_csum_replace(skb, IP_CKSUM_OFF, old_dst, old_src, sizeof(old_src)); - bpf_skb_store_bytes(skb, IP_DST_OFF, &old_src, sizeof(old_src), 0); - - unsigned long long old_src_mac = ethernet->src; - unsigned long long old_dst_mac = ethernet->dst; - ethernet->src = old_dst_mac; - ethernet->dst = old_src_mac; - - #ifdef BPF_TRACE_ICMP_ECHO_REPLY - bpf_trace_printk("[router-%d]: ICMP ECHO Request from 0x%x port %d . Generating Reply ...\n", md->module_id, ip->src, md->in_ifc); - #endif - pkt_redirect(skb, md, md->in_ifc); - return RX_REDIRECT; - } - } - } - } + /* ICMP Echo Responder for router ports */ + if (ip->protocol == IPPROTO_ICMP) { + __be32 l4sum = 0; + struct icmphdr *icmp = data + sizeof(*eth) + sizeof(*ip); + if (data + sizeof(*eth) + sizeof(*ip) + sizeof(*icmp) > data_end) + goto DROP; - /* - decrement TTL and recompute packet checksum (l3 recompute checksum). - if ttl <= 1 DROP the packet. - eventually send ICMP message for the packet dropped. - (maybe to avoid for security reasons) - */ + /* Only manage ICMP Request */ + if (icmp->type == ICMP_ECHO && in_port->ip == ip->daddr) { + // Reply to ICMP Echo request + __u32 old_type = icmp->type; + __u32 new_type = ICMP_ECHOREPLY; + l4sum = bpf_csum_diff(&old_type, 4, &new_type, 4, l4sum); + icmp->type = (__u8) new_type; - __u8 old_ttl = ip->ttl; - __u8 new_ttl; + __be32 old_src = ip->saddr; + __be32 old_dst = ip->daddr; - new_ttl = old_ttl - 1; - bpf_l3_csum_replace(skb, sizeof(*ethernet) + IP_CSUM_OFFSET , old_ttl, new_ttl, sizeof(__u16)); - bpf_skb_store_bytes(skb, sizeof(*ethernet) + IP_TTL_OFFSET , &new_ttl, sizeof(old_ttl), 0); + ip->daddr = old_src; + ip->saddr = old_dst; - #ifdef BPF_TRACE - // bpf_trace_printk("[router-%d]: (after ) ttl: %d checksum: %x\n",ip->ttl,ip->hchecksum); - #endif + __be64 old_src_mac = eth->src; + __be64 old_dst_mac = eth->dst; + eth->src = old_dst_mac; + eth->dst = old_src_mac; - if (new_ttl == 0) { - #ifdef BPF_TRACE - bpf_trace_printk("[router-%d]: packet DROP (ttl = 0)\n", md->module_id); + #ifdef BPF_TRACE_ICMP_ECHO_REPLY + bpf_trace_printk("[router-%d]: ICMP ECHO Request from 0x%x port %d. Generating Reply ...\n", + md->module_id, bpf_htonl(ip->saddr), md->in_ifc); #endif - //Set router port ip address as metadata[0] - u32 mdata[3]; - struct r_port *r_port_p = 0; - r_port_p = router_port.lookup(&md->in_ifc); - u32 ip = 0; - if (r_port_p) { - ip = r_port_p->ip; - } - mdata[0] = ip; - pkt_set_metadata(skb, mdata); - - //Send packet to slowpath - pkt_controller(skb, md, SLOWPATH_TTL_EXCEEDED); - return RX_CONTROLLER; + bpf_l4_csum_replace(skb2, ICMP_CSUM_OFFSET, 0, l4sum, 0); + pkt_redirect(skb, md, md->in_ifc); + return RX_REDIRECT; } + } + + if (ip->ttl == 1) { + #ifdef BPF_TRACE_TTL + bpf_trace_printk("[router-%d]: packet DROP (ttl = 0)\n", md->module_id); + #endif - /* - ROUTING ALGORITHM (simplified) - - for each item in the routing table (upbounded loop) - apply the netmask on dst_ip_address - (possible optimization, not recompute if at next iteration the netmask is the same) - if masked address == network in the routing table - 1- change src mac to otuput port mac - 2- change dst mac to lookup arp table (or send to fffffffffffff) - 3- forward the packet to dst port - */ - - int i = 0; - struct rt_entry *rt_entry_p = 0; - - u32 out_port = 0; - struct r_port *r_port_p = 0; - - #pragma unroll - for (i = 0; i < ROUTING_TABLE_DIM; i++) { - u32 t = i; - rt_entry_p = routing_table.lookup(&t); - if (rt_entry_p) { - if ((ip->dst & rt_entry_p->netmask) == rt_entry_p->network) { - goto FORWARD; - } + // Set router port ip address as metadata[0] + u32 mdata[3]; + mdata[0] = bpf_htonl(in_port->ip); + pkt_set_metadata(skb, mdata); + + // Send packet to slowpath + pkt_controller(skb, md, SLOWPATH_TTL_EXCEEDED); + return RX_CONTROLLER; + } + + /* + ROUTING ALGORITHM (simplified) + + for each item in the routing table (upbounded loop) + apply the netmask on dst_ip_address + (possible optimization, not recompute if at next iteration the netmask is the same) + if masked address == network in the routing table + 1- change src mac to otuput port mac + 2- change dst mac to lookup arp table (or send to fffffffffffff) + 3- forward the packet to dst port + */ + + int i = 0; + struct rt_entry *rt_entry_p = 0; + + #pragma unroll + for (i = 0; i < ROUTING_TABLE_DIM; i++) { + u32 t = i; + rt_entry_p = routing_table.lookup(&t); + if (rt_entry_p) { + if ((ip->daddr & rt_entry_p->netmask) == rt_entry_p->network) { + goto FORWARD; } } + } + + #ifdef BPF_TRACE_ROUTING + bpf_trace_printk("[router-%d]: no routing table match for %x\n", + md->module_id, bpf_htonl(ip->daddr)); + #endif + + goto DROP; - DROP: - #ifdef BPF_LOG - // bpf_trace_printk("[router-%d]: in: %d out: -- DROP\n", md->module_id, md->in_ifc); +FORWARD: ; + #ifdef BPF_TRACE_ROUTING + bpf_trace_printk("[router-%d]: routing table match (#%d) network: %x\n", + md->module_id, i, bpf_htonl(rt_entry_p->network)); + #endif + + // Select out interface + u16 out_port = rt_entry_p->port; + struct r_port *r_port_p = router_port.lookup(&out_port); + if (!r_port_p) { + #ifdef BPF_TRACE_ROUTING + bpf_trace_printk("[router-%d]: Out port '%d' not found\n", + md->module_id, out_port); #endif - return RX_DROP; + goto DROP; + } - FORWARD: - //Select out interface - out_port = rt_entry_p->port; - if (out_port <= 0) - goto DROP; + __be32 dst_ip = 0; + if (rt_entry_p->nexthop == 0) { + // Next Hop is local, directly lookup in arp table for the destination ip. + dst_ip = ip->daddr; + } else { + // Next Hop not local, lookup in arp table for the next hop ip address. + dst_ip = rt_entry_p->nexthop; + } - #ifdef BPF_LOG - bpf_trace_printk("[router-%d]: routing table match (#%d) network: %x\n", - md->module_id, i, rt_entry_p->network); + __be64 *mac_entry = arp_table.lookup(&dst_ip); + if (!mac_entry) { + #ifdef BPF_TRACE_ARP + bpf_trace_printk("[router-%d]: arp lookup failed. Send to controller", md->module_id); #endif - //change src mac - r_port_p = router_port.lookup(&out_port); - if (r_port_p) { - ethernet->src = r_port_p->mac; - } + // Set metadata and send packet to slowpath + u32 mdata[3]; + mdata[0] = bpf_htonl(dst_ip); + mdata[1] = out_port; + mdata[2] = bpf_htonl(r_port_p->ip); - u32 dst_ip = 0; - if (rt_entry_p->nexthop == 0) { - //Next Hop is local, directly lookup in arp table for the destination ip. - dst_ip = ip->dst; - } else { - //Next Hop not local, lookup in arp table for the next hop ip address. - dst_ip = rt_entry_p->nexthop; - } + pkt_set_metadata(skb, mdata); + pkt_controller(skb, md, SLOWPATH_ARP_LOOKUP_MISS); + return RX_CONTROLLER; + } - u64 new_dst_mac = 0xffffffffffff; - u64 *mac_entry = arp_table.lookup(&dst_ip); - if (mac_entry) { - new_dst_mac = *mac_entry; - }else{ - #ifdef BPF_LOG - // bpf_trace_printk("[router-%d]: arp lookup failed. Send to controller",md->module_id); - #endif + #ifdef BPF_TRACE_OUTPUT + bpf_trace_printk("[router-%d]: in: %d out: %d REDIRECT\n", + md->module_id, md->in_ifc, out_port); + #endif - //Set metadata and send packet to slowpath - u32 mdata[3]; - mdata[0] = dst_ip; - mdata[1] = out_port; - r_port_p = router_port.lookup(&out_port); - u32 ip = 0; - if (r_port_p) { - ip = r_port_p->ip; - } - mdata[2] = ip; - pkt_set_metadata(skb, mdata); - pkt_controller(skb, md, SLOWPATH_ARP_LOOKUP_MISS); - return RX_CONTROLLER; - } + eth->dst = *mac_entry; + eth->src = r_port_p->mac; - ethernet->dst = new_dst_mac; + /* Decrement TTL and update checksum */ + __u32 old_ttl = ip->ttl; + __u32 new_ttl = ip->ttl - 1; + l3sum = bpf_csum_diff(&old_ttl, 4, &new_ttl, 4, l3sum); + ip->ttl = (__u8) new_ttl; - #ifdef BPF_TRACE - bpf_trace_printk("[router-%d]: eth_type:%x mac_scr:%lx mac_dst:%lx\n", - md->module_id, ethernet->type, ethernet->src, ethernet->dst); - bpf_trace_printk("[router-%d]: out_ifc: %d\n", out_port); - #endif + bpf_l3_csum_replace(skb2, IP_CSUM_OFFSET, 0, l3sum, 0); - #ifdef BPF_LOG - bpf_trace_printk("[router-%d]: in: %d out: %d REDIRECT\n", md->module_id, md->in_ifc, out_port); + pkt_redirect(skb,md,out_port); + return RX_REDIRECT; + +ARP: ; // arp packet + struct arp_hdr *arp = data + sizeof(*eth); + if (data + sizeof(*eth) + sizeof(*arp) > data_end) + goto DROP; + if (arp->ar_op == bpf_htons(ARPOP_REQUEST) && arp->ar_tip == in_port->ip) { // arp request? + #ifdef BPF_TRACE_ARP + bpf_trace_printk("[arp]: Somebody is asking for my address\n"); #endif - pkt_redirect(skb,md,out_port); - return RX_REDIRECT; + __be64 remotemac = arp->ar_sha; + __be32 remoteip = arp->ar_sip; - ARP: ; //arp packet - struct arp_t *arp = cursor_advance(cursor, sizeof(*arp)); - if (arp->oper == 1) { // arp request? - #ifdef BPF_LOG - bpf_trace_printk("[router-%d]: packet is arp request\n",md->module_id); - #endif - struct r_port *port = router_port.lookup(&md->in_ifc); - if (!port) - return RX_DROP; - if (arp->tpa == port->ip) { - //bpf_trace_printk("[arp]: Somebody is asking for my address\n"); - - /* due to a bcc issue: https://github.com/iovisor/bcc/issues/537 it - * is necessary to copy the data field into a temporal variable - */ - u64 mymac = port->mac; - u64 remotemac = arp->sha; - u32 myip = port->ip; - u32 remoteip = arp->spa; - - ethernet->dst = remotemac; - ethernet->src = mymac; - - /* please note that the mac has to be copied before that the ips. This - * is because the temporal variable used to save the mac has 8 byes, 2 - * more than the mac itself. Then when copying the mac into the packet - * the two first bytes of the ip are also modified. - */ - arp->oper = 2; - arp->tha = remotemac; - arp->sha = mymac; - arp->tpa = remoteip; - arp->spa = myip; - - /* register the requesting mac and ips */ - arp_table.update(&remoteip, &remotemac); - - pkt_redirect(skb, md, md->in_ifc); - return RX_REDIRECT; - - //TODO make sense to send the arp packet to the slowpath - //in order to notify some arp entries updated? - } - } - if (arp->oper == 2) { //arp reply - #ifdef BPF_LOG - bpf_trace_printk("[router-%d]: packet is arp reply\n",md->module_id); + arp->ar_op = bpf_htons(ARPOP_REPLY); + arp->ar_tha = remotemac; + arp->ar_sha = in_port->mac; + arp->ar_tip = remoteip; + arp->ar_sip = in_port->ip; + + eth->dst = remotemac; + eth->src = in_port->mac; + + /* register the requesting mac and ip */ + arp_table.update(&remoteip, &remotemac); + + pkt_redirect(skb, md, md->in_ifc); + return RX_REDIRECT; + } else if (arp->ar_op == bpf_htons(ARPOP_REPLY)) { //arp reply + #ifdef BPF_TRACE_ARP + bpf_trace_printk("[router-%d]: packet is arp reply\n", md->module_id); #endif - struct r_port *port = router_port.lookup(&md->in_ifc); - if (!port){ - return RX_DROP; - }else{ - u64 mac_ = arp->sha; - u32 ip_ = arp->spa; - arp_table.update(&ip_, &mac_); - - //notify the slowpath. New arp reply received. - pkt_controller(skb, md, SLOWPATH_ARP_REPLY); - return RX_CONTROLLER; - } - } + + __be64 mac_ = arp->ar_sha; + __be32 ip_ = arp->ar_sip; + arp_table.update(&ip_, &mac_); + + // notify the slowpath. New arp reply received. + pkt_controller(skb, md, SLOWPATH_ARP_REPLY); + return RX_CONTROLLER; + } + return RX_DROP; + +DROP: + #ifdef BPF_TRACE_OUTPUT + bpf_trace_printk("[router-%d]: in: %d out: -- DROP\n", md->module_id, md->in_ifc); + #endif return RX_DROP; } ` diff --git a/iomodules/router/routerAPI.go b/iomodules/router/routerAPI.go index 0187ba5..b18fe64 100644 --- a/iomodules/router/routerAPI.go +++ b/iomodules/router/routerAPI.go @@ -14,7 +14,6 @@ package router import ( - "bytes" "errors" "fmt" "net" @@ -23,6 +22,8 @@ import ( "github.com/mvbpolito/gosexy/to" + "github.com/iovisor/iovisor-ovn/iomodules" + "github.com/iovisor/iovisor-ovn/hover" l "github.com/op/go-logging" ) @@ -31,7 +32,6 @@ var log = l.MustGetLogger("iomodules-router") type RouterModule struct { ModuleId string - PortsCount int //number of allocated ports RoutingTable []RoutingTableEntry routingTableCount int // number of elements in the routing table Interfaces map[string]*RouterModuleInterface @@ -46,16 +46,15 @@ type RouterModuleInterface struct { IfaceIdRedirectHover int // Iface id inside hover LinkIdHover string // iomodules Link Id IfaceName string - IP string - Netmask string - MAC string + IP net.IP + Netmask net.IPMask + MAC net.HardwareAddr } type RoutingTableEntry struct { - network string - netmask string + network net.IPNet outputIface *RouterModuleInterface - nexthop string + nexthop net.IP } func Create(hc *hover.Client) *RouterModule { @@ -107,10 +106,6 @@ func (r *RouterModule) Destroy() (err error) { return nil } - // TODO: - // All interfaces must be detached before destroying the module. - // Should it be done automatically here, or should be the application responsible for that? - moduleDeleteError, _ := r.hc.ModuleDELETE(r.ModuleId) if moduleDeleteError != nil { log.Errorf("Error in destrying Router IOModule: %s\n", moduleDeleteError) @@ -131,7 +126,7 @@ func (r *RouterModule) AttachExternalInterface(ifaceName string) (err error) { return errors.New(errString) } - if r.PortsCount == 10 { + if len(r.Interfaces) == 32 { errString := "There are not free ports in the router\n" log.Errorf(errString) return errors.New(errString) @@ -143,9 +138,6 @@ func (r *RouterModule) AttachExternalInterface(ifaceName string) (err error) { return linkError } - r.PortsCount++ - - // Saving IfaceIdRedirectHover for this port. The number will be used by security policies ifacenumber := -1 if linkHover.From[0:2] == "m:" { ifacenumber = linkHover.FromId @@ -210,7 +202,7 @@ func (r *RouterModule) AttachToIoModule(ifaceId int, ifaceName string) (err erro return errors.New(errString) } - if r.PortsCount == 10 { + if len(r.Interfaces) == 32 { errString := "There are not free ports in the router" log.Errorf(errString) return errors.New(errString) @@ -234,8 +226,8 @@ func (r *RouterModule) DetachFromIoModule(ifaceName string) (err error) { // After a interface has been added, it is necessary to configure it before // it can be used to route packets //TODO I think we have to add next hop parameter here! -func (r *RouterModule) ConfigureInterface(ifaceName string, ip string, - netmask string, mac string) (err error) { +func (r *RouterModule) ConfigureInterface(ifaceName string, ip net.IP, + netmask net.IPMask, mac net.HardwareAddr) (err error) { if !r.deployed { errString := "Trying to configure an interface in undeployed router" log.Errorf(errString) @@ -251,29 +243,23 @@ func (r *RouterModule) ConfigureInterface(ifaceName string, ip string, return errors.New(errString) } - // TODO: check ip, netmask and mac - - iface.IP = ip - iface.Netmask = netmask - iface.MAC = mac - // configure port entry ifaceIdString := strconv.Itoa(iface.IfaceIdRedirectHover) - ipString := ipToHexadecimalString(ip) - netmaskString := ipToHexadecimalString(netmask) - macString := macToHexadecimalString(mac) + ipString := iomodules.IpToHexBigEndian(ip) + netmaskString := iomodules.NetmaskToHexBigEndian(netmask) + macString := iomodules.MacToHexadecimalStringBigEndian(mac) toSend := ipString + " " + netmaskString + " " + macString r.hc.TableEntryPOST(r.ModuleId, "router_port", ifaceIdString, toSend) - ip_ := net.ParseIP(ip) - netmask_ := ParseIPv4Mask(netmask) - network_ := ip_.Mask(netmask_) + network := ip.Mask(netmask) + + net := net.IPNet{network, netmask} // add route for that port - if r.AddRoutingTableEntryLocal(network_.String(), netmask, ifaceName) != nil { + if r.AddRoutingTableEntryLocal(net, ifaceName) != nil { errString := fmt.Sprintf("Error adding static route for port '%s' in router '%s'\n", ifaceName, r.ModuleId) log.Warningf(errString) @@ -286,19 +272,21 @@ func (r *RouterModule) ConfigureInterface(ifaceName string, ip string, // A local entry of the routing table indicates that the network interface is // directly attached, so there is no need of the next hop address. // This function force the routing table entry to be local, pushing 0 as nexthop -func (r *RouterModule) AddRoutingTableEntryLocal(network string, netmask string, +func (r *RouterModule) AddRoutingTableEntryLocal(network net.IPNet, outputIface string) (err error) { - return r.AddRoutingTableEntry(network, netmask, outputIface, "0.0.0.0") + return r.AddRoutingTableEntry(network, outputIface, net.ParseIP("0.0.0.0")) } // Routes in the routing table have to be ordered according to the netmask length. // This is because of a limitation in the eBPF datapath (no way to perform LPM there) // next hop is a string indicating the ip address of the nexthop, 0.0.0.0 indicates that // the network is directly attached to the router. -func (r *RouterModule) AddRoutingTableEntry(network string, netmask string, - outputIface string, nexthop string) (err error) { +func (r *RouterModule) AddRoutingTableEntry(network net.IPNet, + outputIface string, nexthop net.IP) (err error) { + + log.Infof("add routing table entry: '%s' -> '%d'", network.String(), outputIface) - if r.routingTableCount == 10 { + if r.routingTableCount == 6 { return errors.New("Routing table is full") } @@ -314,7 +302,6 @@ func (r *RouterModule) AddRoutingTableEntry(network string, netmask string, } r.RoutingTable[index].network = network - r.RoutingTable[index].netmask = netmask r.RoutingTable[index].outputIface = iface r.RoutingTable[index].nexthop = nexthop @@ -339,8 +326,8 @@ func (s ByMaskLen) Swap(i, j int) { } func (s ByMaskLen) Less(i, j int) bool { - neti := ParseIPv4Mask(s[i].netmask) - netj := ParseIPv4Mask(s[j].netmask) + neti := s[i].network.Mask + netj := s[j].network.Mask si, _ := neti.Size() sj, _ := netj.Size() @@ -358,19 +345,18 @@ func (r *RouterModule) sendRoutingTable() (err error) { index := 0 for _, i := range r.RoutingTable { - if i.network == "" { + if len(i.network.IP) == 0 { break } stringIndex := strconv.Itoa(index) - toSend := "{" + ipToHexadecimalString(i.network) + " " + - ipToHexadecimalString(i.netmask) + " " + - strconv.Itoa(i.outputIface.IfaceIdRedirectHover) + " " + - ipToHexadecimalString(i.nexthop) + "}" + toSend := iomodules.IpToHexBigEndian(i.network.IP) + " " + + iomodules.NetmaskToHexBigEndian(i.network.Mask) + " " + + "0x" + strconv.FormatUint(uint64(i.outputIface.IfaceIdRedirectHover), 16) + " " + + iomodules.IpToHexBigEndian(i.nexthop) - r.hc.TableEntryPUT(r.ModuleId, "routing_table", - stringIndex, toSend) + r.hc.TableEntryPOST(r.ModuleId, "routing_table", stringIndex, toSend) index++ } @@ -383,15 +369,16 @@ func (r *RouterModule) sendRoutingTable() (err error) { // //} -func (r *RouterModule) AddArpEntry(ip string, mac string) (err error) { +func (r *RouterModule) AddArpEntry(ip net.IP, mac net.HardwareAddr) (err error) { if !r.deployed { errString := "Trying to add arp entry in undeployed router" log.Errorf(errString) return errors.New(errString) } - r.hc.TableEntryPUT(r.ModuleId, "arp_table", - ipToHexadecimalString(ip), macToHexadecimalString(mac)) + r.hc.TableEntryPOST(r.ModuleId, "arp_table", + iomodules.IpToHexBigEndian(ip), + iomodules.MacToHexadecimalStringBigEndian(mac)) return nil } @@ -418,20 +405,26 @@ func (r *RouterModule) Configure(conf interface{}) (err error) { entryMap := to.Map(entry) name, ok1 := entryMap["name"] - ip, ok2 := entryMap["ip"] - netmask, ok3 := entryMap["netmask"] - mac, ok4 := entryMap["mac"] + ip_, ok2 := entryMap["ip"] + netmask_, ok3 := entryMap["netmask"] + mac_, ok4 := entryMap["mac"] if !ok1 || !ok2 || !ok3 || !ok4 { log.Errorf("Skipping non valid interface") continue } + ip := net.ParseIP(ip_.(string)) + netmask := iomodules.ParseIPv4Mask(netmask_.(string)) + mac, err := net.ParseMAC(mac_.(string)) + if err != nil { + return errors.New("'%s' is a not valid mac") + } + log.Infof("Configuring Interface '%s', '%s', '%s', '%s'", - name.(string), ip.(string), netmask.(string), mac.(string)) + name.(string), ip.String(), netmask.String(), mac.String()) - err := r.ConfigureInterface(name.(string), ip.(string), - netmask.(string), mac.(string)) + err = r.ConfigureInterface(name.(string), ip, netmask, mac) if err != nil { return err } @@ -443,10 +436,10 @@ func (r *RouterModule) Configure(conf interface{}) (err error) { for _, entry := range to.List(static_routes) { entryMap := to.Map(entry) - network, ok1 := entryMap["network"] - netmask, ok2 := entryMap["netmask"] + network_, ok1 := entryMap["network"] + netmask_, ok2 := entryMap["netmask"] interface_, ok3 := entryMap["interface"] - next_hop, ok4 := entryMap["next_hop"] + next_hop_, ok4 := entryMap["next_hop"] if !ok1 || !ok2 || !ok3 { log.Errorf("Skipping non valid static route") @@ -454,14 +447,19 @@ func (r *RouterModule) Configure(conf interface{}) (err error) { } if !ok4 { - next_hop = "0.0.0.0" + next_hop_ = "0.0.0.0" } + network := net.ParseIP(network_.(string)) + netmask := iomodules.ParseIPv4Mask(netmask_.(string)) + next_hop := net.ParseIP(next_hop_.(string)) + + net := net.IPNet{network, netmask} + log.Infof("Adding Static Route: '%s', '%s', '%s', '%s'", - network.(string), netmask.(string), interface_.(string), next_hop.(string)) + net.String(), interface_.(string), next_hop.String()) - err := r.AddRoutingTableEntry(network.(string), netmask.(string), - interface_.(string), next_hop.(string)) + err := r.AddRoutingTableEntry(net, interface_.(string), next_hop) if err != nil { return err } @@ -473,18 +471,24 @@ func (r *RouterModule) Configure(conf interface{}) (err error) { for _, entry := range to.List(arp_entries) { entryMap := to.Map(entry) - ip, ok1 := entryMap["ip"] - mac, ok2 := entryMap["mac"] + ip_, ok1 := entryMap["ip"] + mac_, ok2 := entryMap["mac"] if !ok1 || !ok2 { log.Errorf("Skipping non valid arp entry") continue } + ip := net.ParseIP(ip_.(string)) + mac, err := net.ParseMAC(mac_.(string)) + if err != nil { + return errors.New("no valid mac") + } + log.Infof("Adding arp entry Route: '%s' -> '%s'", - ip.(string), mac.(string)) + ip.String(), mac.String()) - err := r.AddArpEntry(ip.(string), mac.(string)) + err = r.AddArpEntry(ip, mac) if err != nil { return err } @@ -493,38 +497,3 @@ func (r *RouterModule) Configure(conf interface{}) (err error) { return nil } - -// TODO: this function should be smarter -func macToHexadecimalString(s string) string { - var buffer bytes.Buffer - - buffer.WriteString("0x") - buffer.WriteString(s[0:2]) - buffer.WriteString(s[3:5]) - buffer.WriteString(s[6:8]) - buffer.WriteString(s[9:11]) - buffer.WriteString(s[12:14]) - buffer.WriteString(s[15:17]) - - return buffer.String() -} - -func ipToHexadecimalString(ip string) string { - - trial := net.ParseIP(ip) - if trial.To4() != nil { - ba := []byte(trial.To4()) - ipv4HexStr := fmt.Sprintf("0x%02x%02x%02x%02x", ba[0], ba[1], ba[2], ba[3]) - return ipv4HexStr - } - - return "" -} - -func ParseIPv4Mask(s string) net.IPMask { - mask := net.ParseIP(s) - if mask == nil { - return nil - } - return net.IPv4Mask(mask[12], mask[13], mask[14], mask[15]) -} diff --git a/iomodules/utils.go b/iomodules/utils.go index 5bd9601..eedb5de 100644 --- a/iomodules/utils.go +++ b/iomodules/utils.go @@ -79,3 +79,8 @@ func ParseIPv4Mask(s string) net.IPMask { } return net.IPv4Mask(mask[12], mask[13], mask[14], mask[15]) } + +func NetmaskToHexBigEndian(netmask net.IPMask) string { + //ip := net.ParseIP(netmask.String()) + return IpToHexBigEndian(net.IP(netmask)) +} diff --git a/mainlogic/mainlogic.go b/mainlogic/mainlogic.go index 501df4d..5ae8fcb 100644 --- a/mainlogic/mainlogic.go +++ b/mainlogic/mainlogic.go @@ -311,7 +311,16 @@ func updatePort(sw *L2Switch, lport *ovnmonitor.LogicalSwitchPort) { if lrp.IP != "" { // configure router - err := r.rIoModule.ConfigureInterface(lrp.Name, lrp.IP, lrp.Mask, lrp.Mac) + ip := net.ParseIP(lrp.IP) + mac, err := net.ParseMAC(lrp.Mac) + if err != nil { + log.Errorf("Error configuring router: Non valid mac") + return + } + + mask := iomodules.ParseIPv4Mask(lrp.Mask) + + err = r.rIoModule.ConfigureInterface(lrp.Name, ip, mask, mac) if err != nil { log.Errorf("Error configuring router") } @@ -320,11 +329,6 @@ func updatePort(sw *L2Switch, lport *ovnmonitor.LogicalSwitchPort) { // avoid problems with the broadcast. // (this issue will be solved soon) // Mac address of the router is present through this interface - mac, err := net.ParseMAC(lrp.Mac) - if err != nil { - log.Errorf("Error configuring router: Non valid mac") - return - } sw.swIomodule.AddForwardingTableEntry(mac, port.Name) } } diff --git a/mainlogic/print.go b/mainlogic/print.go index 4714ce8..b5e244e 100644 --- a/mainlogic/print.go +++ b/mainlogic/print.go @@ -80,7 +80,7 @@ func PrintRouter(name string) { if r.rIoModule != nil { // table = tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"}) - table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(r.rIoModule.PortsCount)}) + table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(len(r.rIoModule.Interfaces))}) table.Render() } else { // table = tablewriter.NewWriter(os.Stdout) @@ -100,7 +100,7 @@ func PrintRouter(name string) { table = tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"ROUTER", "NAME", "LINK", "FD", "REDIRECT", "IP", "NETMASK", "MAC"}) for _, iface := range r.rIoModule.Interfaces { - table.Append([]string{r.Name, iface.IfaceName, iface.LinkIdHover, strconv.Itoa(iface.IfaceIdRedirectHover), iface.IP, iface.Netmask, iface.IP}) + table.Append([]string{r.Name, iface.IfaceName, iface.LinkIdHover, strconv.Itoa(iface.IfaceIdRedirectHover), iface.IP.String(), iface.Netmask.String(), iface.IP.String()}) } table.Render() } @@ -112,7 +112,7 @@ func PrintRouters(verbose bool) { table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"}) for _, r := range routers { - table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(r.rIoModule.PortsCount)}) + table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(len(r.rIoModule.Interfaces))}) } table.Render()