unsigned int core_4to6(struct sk_buff *skb) { struct packet pkt; struct iphdr *hdr = ip_hdr(skb); int error; if (config_get_is_disable()) return NF_ACCEPT; /* Translation is disabled; let the packet pass. */ if (is_blacklisted4(hdr->saddr) || is_blacklisted4(hdr->daddr)) return NF_ACCEPT; if (nat64_is_stateful()) { if (!pool4_contains(hdr->daddr) || pool6_is_empty()) return NF_ACCEPT; /* Not meant for translation; let the kernel handle it. */ } log_debug("==============================================="); log_debug("Catching IPv4 packet: %pI4->%pI4", &hdr->saddr, &hdr->daddr); error = pkt_init_ipv4(&pkt, skb); /* Reminder: This function might change pointers. */ if (error) return NF_DROP; error = validate_icmp4_csum(&pkt); if (error) { inc_stats(&pkt, IPSTATS_MIB_INHDRERRORS); return NF_DROP; } return core_common(&pkt); }
unsigned int core_4to6(struct sk_buff *skb) { struct iphdr *ip4_header; struct in_addr daddr; enum verdict result; skb_linearize(skb); ip4_header = ip_hdr(skb); daddr.s_addr = ip4_header->daddr; if (!pool4_contains(&daddr)) return NF_ACCEPT; log_debug("==============================================="); log_debug("Catching IPv4 packet: %pI4->%pI4", &ip4_header->saddr, &ip4_header->daddr); result = validate_skb_ipv4(skb); if (result != VER_CONTINUE) return result; return nat64_core(skb, compute_out_tuple_4to6, translating_the_packet_4to6, send_packet_ipv6); }
/** * Main F&U routine. Called during the processing of every packet. * * Decides if "skb" should be processed, updating binding and session information. * * @param[in] skb packet being translated. * @param[in] tuple skb's summary. * @return indicator of what should happen to skb. */ verdict filtering_and_updating(struct packet *pkt, struct tuple *in_tuple) { struct ipv6hdr *hdr_ip6; verdict result = VERDICT_CONTINUE; log_debug("Step 2: Filtering and Updating"); switch (pkt_l3_proto(pkt)) { case L3PROTO_IPV6: /* ICMP errors should not be filtered or affect the tables. */ if (pkt_is_icmp6_error(pkt)) { log_debug("Packet is ICMPv6 error; skipping step..."); return VERDICT_CONTINUE; } /* Get rid of hairpinning loops and unwanted packets. */ hdr_ip6 = pkt_ip6_hdr(pkt); if (pool6_contains(&hdr_ip6->saddr)) { log_debug("Hairpinning loop. Dropping..."); inc_stats(pkt, IPSTATS_MIB_INADDRERRORS); return VERDICT_DROP; } if (!pool6_contains(&hdr_ip6->daddr)) { log_debug("Packet was rejected by pool6; dropping..."); inc_stats(pkt, IPSTATS_MIB_INADDRERRORS); return VERDICT_DROP; } break; case L3PROTO_IPV4: /* ICMP errors should not be filtered or affect the tables. */ if (pkt_is_icmp4_error(pkt)) { log_debug("Packet is ICMPv4 error; skipping step..."); return VERDICT_CONTINUE; } /* Get rid of unexpected packets */ if (!pool4_contains(pkt_ip4_hdr(pkt)->daddr)) { log_debug("Packet was rejected by pool4; dropping..."); inc_stats(pkt, IPSTATS_MIB_INADDRERRORS); return VERDICT_DROP; } break; } /* Process packet, according to its protocol. */ switch (pkt_l4_proto(pkt)) { case L4PROTO_UDP: switch (pkt_l3_proto(pkt)) { case L3PROTO_IPV6: result = ipv6_simple(pkt, in_tuple); break; case L3PROTO_IPV4: result = ipv4_simple(pkt, in_tuple); break; } break; case L4PROTO_TCP: result = tcp(pkt, in_tuple); break; case L4PROTO_ICMP: switch (pkt_l3_proto(pkt)) { case L3PROTO_IPV6: if (config_get_filter_icmpv6_info()) { log_debug("Packet is ICMPv6 info (ping); dropping due to policy."); inc_stats(pkt, IPSTATS_MIB_INDISCARDS); return VERDICT_DROP; } result = ipv6_simple(pkt, in_tuple); break; case L3PROTO_IPV4: result = ipv4_simple(pkt, in_tuple); break; } break; case L4PROTO_OTHER: WARN(true, "Unknown layer 4 protocol (%d)...", pkt_l4_proto(pkt)); break; } log_debug("Done: Step 2."); return result; }
/** * Checks whether "pkt" is a hairpin packet. * * @param pkt outgoing packet the NAT64 would send if it's not a hairpin. * @return whether pkt is a hairpin packet. */ bool is_hairpin(struct packet *pkt) { return (pkt_l3_proto(pkt) == L3PROTO_IPV4) ? pool4_contains(pkt_ip4_hdr(pkt)->daddr) : false; }
int add_static_route(struct request_bib *req) { struct bib_entry *bib_by_ipv6, *bib_by_ipv4; struct bib_entry *bib = NULL; int error; if (!pool4_contains(&req->add.ipv4.address)) { log_err(ERR_POOL6_NOT_FOUND, "The address '%pI4' does not belong to the IPv4 pool.", &req->add.ipv4.address); return -EINVAL; } spin_lock_bh(&bib_session_lock); /* Check if the BIB entry exists. */ error = bib_get_by_ipv6(&req->add.ipv6, req->l4_proto, &bib_by_ipv6); if (!error) { bib = bib_by_ipv6; goto already_mapped; } if (error != -ENOENT) goto generic_error; error = bib_get_by_ipv4(&req->add.ipv4, req->l4_proto, &bib_by_ipv4); if (!error) { bib = bib_by_ipv4; goto already_mapped; } if (error != -ENOENT) goto generic_error; /* Borrow the address and port from the IPv4 pool. */ if (is_error(pool4_get(req->l4_proto, &req->add.ipv4))) { /* * This might happen if Filtering just reserved the address#port, but hasn't yet inserted * the BIB entry to the table. This is because bib_session_lock doesn't cover the IPv4 * pool. * Otherwise something's not returning borrowed address#ports to the pool, which is an * error. */ log_err(ERR_BIB_REINSERT, "Port number %u from address %pI4 is taken from the IPv4 pool, " "but it wasn't found in the BIB. Please try again; if the problem persists, " "please report.", req->add.ipv4.l4_id, &req->add.ipv4.address); error = -EEXIST; goto failure; } /* Create and insert the entry. */ bib = bib_create(&req->add.ipv4, &req->add.ipv6, true); if (!bib) { log_err(ERR_ALLOC_FAILED, "Could NOT allocate a BIB entry."); error = -ENOMEM; goto failure; } error = bib_add(bib, req->l4_proto); if (error) { log_err(ERR_UNKNOWN_ERROR, "Could NOT add the BIB entry to the table."); goto failure; } spin_unlock_bh(&bib_session_lock); return 0; already_mapped: log_err(ERR_BIB_REINSERT, "%pI6c#%u is already mapped to %pI4#%u.", &bib->ipv6.address, bib->ipv6.l4_id, &bib->ipv4.address, bib->ipv4.l4_id); error = -EEXIST; bib = NULL; goto failure; generic_error: log_err(ERR_UNKNOWN_ERROR, "Error code %u while trying to interact with the BIB.", error); /* Fall through. */ failure: if (bib) bib_kfree(bib); spin_unlock_bh(&bib_session_lock); return error; }
/** * Checks whether "pkt" is a hairpin packet. * * @param pkt outgoing packet the NAT64 would send if it's not a hairpin. * @return whether pkt is a hairpin packet. */ bool is_hairpin(struct sk_buff *skb) { return (skb_l3_proto(skb) == L3PROTO_IPV4) ? pool4_contains(ip_hdr(skb)->daddr) : false; }
/** * Main F&U routine. Called during the processing of every packet. * * Decides if "skb" should be processed, updating binding and session information. * * @param[in] skb packet being translated. * @param[in] tuple skb's summary. * @return indicator of what should happen to skb. */ verdict filtering_and_updating(struct sk_buff* skb, struct tuple *in_tuple) { struct ipv6hdr *hdr_ip6; verdict result = VER_CONTINUE; log_debug("Step 2: Filtering and Updating"); switch (skb_l3_proto(skb)) { case L3PROTO_IPV6: /* ICMP errors should not be filtered or affect the tables. */ if (skb_l4_proto(skb) == L4PROTO_ICMP && is_icmp6_error(icmp6_hdr(skb)->icmp6_type)) { log_debug("Packet is ICMPv6 error; skipping step..."); return VER_CONTINUE; } /* Get rid of hairpinning loops and unwanted packets. */ hdr_ip6 = ipv6_hdr(skb); if (pool6_contains(&hdr_ip6->saddr)) { log_debug("Hairpinning loop. Dropping..."); inc_stats(skb, IPSTATS_MIB_INADDRERRORS); return VER_DROP; } if (!pool6_contains(&hdr_ip6->daddr)) { log_debug("Packet was rejected by pool6; dropping..."); inc_stats(skb, IPSTATS_MIB_INADDRERRORS); return VER_DROP; } break; case L3PROTO_IPV4: /* ICMP errors should not be filtered or affect the tables. */ if (skb_l4_proto(skb) == L4PROTO_ICMP && is_icmp4_error(icmp_hdr(skb)->type)) { log_debug("Packet is ICMPv4 error; skipping step..."); return VER_CONTINUE; } /* Get rid of unexpected packets */ if (!pool4_contains(ip_hdr(skb)->daddr)) { log_debug("Packet was rejected by pool4; dropping..."); inc_stats(skb, IPSTATS_MIB_INADDRERRORS); return VER_DROP; } break; } /* Process packet, according to its protocol. */ switch (skb_l4_proto(skb)) { case L4PROTO_UDP: switch (skb_l3_proto(skb)) { case L3PROTO_IPV6: result = ipv6_simple(skb, in_tuple); break; case L3PROTO_IPV4: result = ipv4_simple(skb, in_tuple); break; } break; case L4PROTO_TCP: result = tcp(skb, in_tuple); break; case L4PROTO_ICMP: switch (skb_l3_proto(skb)) { case L3PROTO_IPV6: if (filter_icmpv6_info()) { log_debug("Packet is ICMPv6 info (ping); dropping due to policy."); inc_stats(skb, IPSTATS_MIB_INDISCARDS); return VER_DROP; } result = ipv6_simple(skb, in_tuple); break; case L3PROTO_IPV4: result = ipv4_simple(skb, in_tuple); break; } break; } log_debug("Done: Step 2."); return result; }