/*--------------------------------------------------------------------- * Method: do_nat_interal * * Scope: Local * * This function performs the decision logic for packets arriving on an * external interface. it determines if the NAT has should translate the * packet and route it to the internal interface, whether it should drop * the packet, or whether it should let the router route the packet or * process it as usual * * parameters: * sr - a reference to the router structure * iphdr - a pointer to the packet received * iface - the interface through which the packet was received * *---------------------------------------------------------------------*/ nat_action_type do_nat_external(struct sr_instance *sr, sr_ip_hdr_t *iphdr, sr_if_t *iface) { DebugNAT("+++ Applying external NAT interface logic +++\n"); if (destined_to_nat_external(sr,iphdr->ip_dst)) { DebugNAT("+++ Inbound packet destined to NAT. handling... +++\n"); //destined to nat and/or private network behind it if (iphdr->ip_p == ip_protocol_icmp) //ICMP return handle_incoming_icmp(&sr->nat,iphdr); if (iphdr->ip_p == ip_protocol_tcp) //TCP return handle_incoming_tcp(&sr->nat,iphdr); return nat_action_drop; //drop packet if not TCP/ICMP } sr_rt_t *best_match = NULL; if (!longest_prefix_match(sr->routing_table, iphdr->ip_dst,&best_match)) { DebugNAT("+++ No entry in routing table. no action required +++\n\n"); return nat_action_route; //no match in routing table. need to generate ICMP host unreachable //no action required on behalf of the NAT //return route. let router figure out what he needs to do. } if (strcmp(best_match->interface,iface->name)==0) { DebugNAT("+++ Routing back on same interface: external->external. no action required +++\n"); return nat_action_route; //routing back on same interface: external->external. //no action required on behalf of the NAT } DebugNAT("+++ Packet destined directly to internal interface. refuse request +++\n"); return nat_action_unrch; //ICMP host unreachable should be sent to packets trying to //access hosts behind the NAT directly }
/*--------------------------------------------------------------------- * Method: do_nat_interal * * Scope: Local * * This function performs the decision logic for packets arriving on an * internal interface. it determines if the NAT has to process the packet * and create a mapping, or simply let the router route or process the * packet as usual. * * parameters: * sr - a reference to the router structure * iphdr - a pointer to the packet received * iface - the interface through which the packet was received * *---------------------------------------------------------------------*/ nat_action_type do_nat_internal(struct sr_instance *sr, sr_ip_hdr_t *iphdr, sr_if_t *iface) { DebugNAT("+++ Applying internal NAT interface logic +++\n"); if (destined_to_nat_external(sr,iphdr->ip_dst)) { //hairpinning not supported DebugNAT("+++ Potential hairpinning detected. assuming NAT is final destination. +++\n"); return nat_action_route; } sr_rt_t *best_match = NULL; if (!longest_prefix_match(sr->routing_table, iphdr->ip_dst,&best_match)) { DebugNAT("+++ No entry in routing table. no action required +++\n"); return nat_action_route; //no match in routing table. need to generate ICMP host unreachable //no action required on behalf of the NAT. no objection by the nat //to routing the packet. let router figure out his response } if (strcmp(best_match->interface,iface->name)==0) { DebugNAT("+++ Routing back on same interface: internal->internal. no action required +++\n"); return nat_action_route; //routing back on same interface: internal->internal. //no action required on behalf of the NAT } DebugNAT("+++ Outbound packet crossing NAT. handling... +++\n"); //packet crossing the NAT outbound if (iphdr->ip_p == ip_protocol_icmp) //ICMP return handle_outgoing_icmp(sr,iphdr); if (iphdr->ip_p == ip_protocol_tcp) //TCP return handle_outgoing_tcp(sr,iphdr); return nat_action_drop; //drop packet if not TCP/ICMP }
/* Called from syscall or from eBPF program */ static void *trie_lookup_elem(struct bpf_map *map, void *_key) { struct lpm_trie *trie = container_of(map, struct lpm_trie, map); struct lpm_trie_node *node, *found = NULL; struct bpf_lpm_trie_key *key = _key; /* Start walking the trie from the root node ... */ for (node = rcu_dereference(trie->root); node;) { unsigned int next_bit; size_t matchlen; /* Determine the longest prefix of @node that matches @key. * If it's the maximum possible prefix for this trie, we have * an exact match and can return it directly. */ matchlen = longest_prefix_match(trie, node, key); if (matchlen == trie->max_prefixlen) { found = node; break; } /* If the number of bits that match is smaller than the prefix * length of @node, bail out and return the node we have seen * last in the traversal (ie, the parent). */ if (matchlen < node->prefixlen) break; /* Consider this node as return candidate unless it is an * artificially added intermediate one. */ if (!(node->flags & LPM_TREE_NODE_FLAG_IM)) found = node; /* If the node match is fully satisfied, let's see if we can * become more specific. Determine the next bit in the key and * traverse down. */ next_bit = extract_bit(key->data, node->prefixlen); node = rcu_dereference(node->child[next_bit]); } if (!found) return NULL; return found->data + trie->data_size; }
void test_match(struct sr_instance *sr,int a, int b, int c, int d, bool result, char * correct_match){ in_addr_t x; char *y = (char *)&x; /* so that we can address the individual bytes */ y[0] = a; y[1] = b; y[2] = c; y[3] = d; struct in_addr ip = *(struct in_addr *)&x; struct sr_rt * match = longest_prefix_match(sr, (uint32_t)ip.s_addr); assert(match && strcmp(match->interface, correct_match) == 0); }
/* Called from syscall or from eBPF program */ static int trie_update_elem(struct bpf_map *map, void *_key, void *value, u64 flags) { struct lpm_trie *trie = container_of(map, struct lpm_trie, map); struct lpm_trie_node *node, *im_node = NULL, *new_node = NULL; struct lpm_trie_node __rcu **slot; struct bpf_lpm_trie_key *key = _key; unsigned long irq_flags; unsigned int next_bit; size_t matchlen = 0; int ret = 0; if (unlikely(flags > BPF_EXIST)) return -EINVAL; if (key->prefixlen > trie->max_prefixlen) return -EINVAL; raw_spin_lock_irqsave(&trie->lock, irq_flags); /* Allocate and fill a new node */ if (trie->n_entries == trie->map.max_entries) { ret = -ENOSPC; goto out; } new_node = lpm_trie_node_alloc(trie, value); if (!new_node) { ret = -ENOMEM; goto out; } trie->n_entries++; new_node->prefixlen = key->prefixlen; RCU_INIT_POINTER(new_node->child[0], NULL); RCU_INIT_POINTER(new_node->child[1], NULL); memcpy(new_node->data, key->data, trie->data_size); /* Now find a slot to attach the new node. To do that, walk the tree * from the root and match as many bits as possible for each node until * we either find an empty slot or a slot that needs to be replaced by * an intermediate node. */ slot = &trie->root; while ((node = rcu_dereference_protected(*slot, lockdep_is_held(&trie->lock)))) { matchlen = longest_prefix_match(trie, node, key); if (node->prefixlen != matchlen || node->prefixlen == key->prefixlen || node->prefixlen == trie->max_prefixlen) break; next_bit = extract_bit(key->data, node->prefixlen); slot = &node->child[next_bit]; } /* If the slot is empty (a free child pointer or an empty root), * simply assign the @new_node to that slot and be done. */ if (!node) { rcu_assign_pointer(*slot, new_node); goto out; } /* If the slot we picked already exists, replace it with @new_node * which already has the correct data array set. */ if (node->prefixlen == matchlen) { new_node->child[0] = node->child[0]; new_node->child[1] = node->child[1]; if (!(node->flags & LPM_TREE_NODE_FLAG_IM)) trie->n_entries--; rcu_assign_pointer(*slot, new_node); kfree_rcu(node, rcu); goto out; } /* If the new node matches the prefix completely, it must be inserted * as an ancestor. Simply insert it between @node and *@slot. */ if (matchlen == key->prefixlen) { next_bit = extract_bit(node->data, matchlen); rcu_assign_pointer(new_node->child[next_bit], node); rcu_assign_pointer(*slot, new_node); goto out; } im_node = lpm_trie_node_alloc(trie, NULL); if (!im_node) { ret = -ENOMEM; goto out; } im_node->prefixlen = matchlen; im_node->flags |= LPM_TREE_NODE_FLAG_IM; memcpy(im_node->data, node->data, trie->data_size); /* Now determine which child to install in which slot */ if (extract_bit(key->data, matchlen)) { rcu_assign_pointer(im_node->child[0], node); rcu_assign_pointer(im_node->child[1], new_node); } else { rcu_assign_pointer(im_node->child[0], new_node); rcu_assign_pointer(im_node->child[1], node); } /* Finally, assign the intermediate node to the determined spot */ rcu_assign_pointer(*slot, im_node); out: if (ret) { if (new_node) trie->n_entries--; kfree(new_node); kfree(im_node); } raw_spin_unlock_irqrestore(&trie->lock, irq_flags); return ret; }