/** * Mirrors the core's behavior by processing skb_in as if it was the incoming packet. * * @param skb_in the outgoing packet. Except because it's a hairpin, here it's treated as if it was * the one received from the network. * @param tuple_in skb_in's tuple. * @return whether we managed to U-turn the packet successfully. */ verdict handling_hairpinning(struct sk_buff *skb_in, struct tuple *tuple_in) { struct sk_buff *skb_out; struct tuple tuple_out; verdict result; log_debug("Step 5: Handling Hairpinning..."); if (skb_l4_proto(skb_in) == L4PROTO_ICMP) { /* RFC 6146 section 2 (Definition of "Hairpinning"). */ log_debug("ICMP is not supported by hairpinning. Dropping packet..."); return VER_DROP; } result = filtering_and_updating(skb_in, tuple_in); if (result != VER_CONTINUE) return result; result = compute_out_tuple(tuple_in, &tuple_out, skb_in); if (result != VER_CONTINUE) return result; result = translating_the_packet(&tuple_out, skb_in, &skb_out); if (result != VER_CONTINUE) return result; result = sendpkt_send(skb_in, skb_out); if (result != VER_CONTINUE) return result; log_debug("Done step 5."); return VER_CONTINUE; }
static unsigned int core_common(struct packet *in) { struct packet out; struct tuple tuple_in; struct tuple tuple_out; verdict result; result = determine_in_tuple(in, &tuple_in); if (result != VERDICT_CONTINUE) goto end; result = filtering_and_updating(in, &tuple_in); if (result != VERDICT_CONTINUE) goto end; result = compute_out_tuple(&tuple_in, &tuple_out, in); if (result != VERDICT_CONTINUE) goto end; result = translating_the_packet(&tuple_out, in, &out); if (result != VERDICT_CONTINUE) goto end; if (is_hairpin(&out)) { result = handling_hairpinning(&out, &tuple_out); kfree_skb(out.skb); } else { result = sendpkt_send(in, &out); /* send_pkt releases skb_out regardless of verdict. */ } if (result != VERDICT_CONTINUE) goto end; log_debug("Success."); /* * The new packet was sent, so the original one can die; drop it. * * NF_DROP translates into an error (see nf_hook_slow()). * Sending a replacing & translated version of the packet should not count as an error, * so we free the incoming packet ourselves and return NF_STOLEN on success. */ kfree_skb(in->skb); result = VERDICT_STOLEN; /* Fall through. */ end: if (result == VERDICT_ACCEPT) log_debug("Returning the packet to the kernel."); return (unsigned int) result; }
/** * Mirrors the core's behavior by processing skb_in as if it was the incoming packet. * * @param skb_in the outgoing packet. Except because it's a hairpin, here it's treated as if it was * the one received from the network. * @param tuple_in skb_in's tuple. * @return whether we managed to U-turn the packet successfully. */ verdict handling_hairpinning(struct packet *in, struct tuple *tuple_in) { struct packet out; struct tuple tuple_out; verdict result; log_debug("Step 5: Handling Hairpinning..."); if (pkt_l4_proto(in) == L4PROTO_ICMP) { /* * RFC 6146 section 2 (Definition of "Hairpinning"). * * Update 2014-11-21: * Actually, since ICMP errors count as UDP or TCP packets tuple-wise, maybe the RFC means * we should only filter out ICMP echoes. * Or maybe not even that, since they're going to be dropped later anyway, once Jool fails * to find the mapping. * Unfortunately, if I remove this if, Jool crashes when I hairpin a ICMP error. */ log_debug("ICMP is not supported by hairpinning. Dropping packet..."); return VERDICT_DROP; } result = filtering_and_updating(in, tuple_in); if (result != VERDICT_CONTINUE) return result; result = compute_out_tuple(tuple_in, &tuple_out, in); if (result != VERDICT_CONTINUE) return result; result = translating_the_packet(&tuple_out, in, &out); if (result != VERDICT_CONTINUE) return result; result = sendpkt_send(in, &out); if (result != VERDICT_CONTINUE) return result; log_debug("Done step 5."); return VERDICT_CONTINUE; }
static bool test_4to6(l4_protocol l4_proto) { struct tuple in, out; int field = 0; bool success = true; if (is_error(init_ipv4_tuple(&in, remote4, 80, local4, 5678, l4_proto))) return false; success &= assert_equals_int(VER_CONTINUE, compute_out_tuple(&in, &out, NULL), "Call"); success &= assert_equals_int(L3PROTO_IPV6, out.l3_proto, "l3 proto"); success &= assert_equals_int(l4_proto, out.l4_proto, "l4 proto"); success &= assert_equals_ipv6_str(local6, &out.src.addr6.l3, "src addr"); if (l4_proto == L4PROTO_ICMP) success &= assert_equals_u16(1234, out.src.addr6.l4, "src port (icmp id)"); else success &= assert_equals_u16(80, out.src.addr6.l4, "src port"); success &= assert_equals_ipv6_str(remote6, &out.dst.addr6.l3, "dst addr"); success &= assert_equals_u16(1234, out.dst.addr6.l4, "dst port"); success &= assert_equals_int(0, field, "unchanged field"); return success; }