bool nat64_determine_incoming_tuple(struct sk_buff *skb, struct nf_conntrack_tuple **result) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; enum ip_conntrack_dir dir; struct nf_conntrack_tuple *tuple; log_debug("Step 1: Determining the Incoming Tuple"); // Conntrack already built the tuple, so just ask. ct = nf_ct_get(skb, &ctinfo); if (ct == NULL) { log_warning(" Packet does not contain a conntrack entry. Dropping..."); return false; } dir = CTINFO2DIR(ctinfo); tuple = &ct->tuplehash[dir].tuple; log_tuple(tuple); // Now perform the only validation defined in this step. switch (tuple->l3_protocol) { case NFPROTO_IPV4: if (!is_l4_protocol_supported_ipv4(tuple->l4_protocol)) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); goto unsupported_l4_protocol; } break; case NFPROTO_IPV6: if (!is_l4_protocol_supported_ipv6(tuple->l4_protocol)) { icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); goto unsupported_l4_protocol; } break; default: goto unsupported_l3_protocol; } *result = tuple; log_debug("Done step 1."); return true; unsupported_l3_protocol: log_warning(" Unsupported L3 protocol (%u). Dropping packet...", tuple->l3_protocol); return false; unsupported_l4_protocol: log_warning(" Unsupported L4 protocol (%u). Dropping packet...", tuple->l4_protocol); return false; }
static bool test_address_filtering_aux(int src_addr_id, int src_port_id, int dst_addr_id, int dst_port_id) { struct tuple tuple4; tuple4.src.addr4.l3 = addr4[src_addr_id].l3; tuple4.dst.addr4.l3 = addr4[dst_addr_id].l3; tuple4.src.addr4.l4 = addr4[src_port_id].l4; tuple4.dst.addr4.l4 = addr4[dst_port_id].l4; tuple4.l4_proto = L4PROTO_UDP; tuple4.l3_proto = L3PROTO_IPV4; log_tuple(&tuple4); return sessiondb_allow(&tuple4); }
bool determine_in_tuple(struct sk_buff *skb, struct tuple *tuple) { struct iphdr *hdr4; struct ipv6hdr *hdr6; struct icmphdr *icmp4; struct icmp6hdr *icmp6; struct hdr_iterator iterator; log_debug("Step 1: Determining the Incoming Tuple"); switch (be16_to_cpu(skb->protocol)) { case ETH_P_IP: hdr4 = ip_hdr(skb); switch (hdr4->protocol) { case IPPROTO_UDP: if (!ipv4_udp(hdr4, ipv4_extract_l4_hdr(hdr4), tuple)) return false; break; case IPPROTO_TCP: if (!ipv4_tcp(hdr4, ipv4_extract_l4_hdr(hdr4), tuple)) return false; break; case IPPROTO_ICMP: icmp4 = ipv4_extract_l4_hdr(hdr4); if (is_icmp4_info(icmp4->type)) { if (!ipv4_icmp_info(hdr4, icmp4, tuple)) return false; } else { if (!ipv4_icmp_err(hdr4, icmp4, tuple)) return false; } break; default: log_info("Unsupported transport protocol for IPv4: %d.", hdr4->protocol); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); return false; } break; case ETH_P_IPV6: hdr6 = ipv6_hdr(skb); hdr_iterator_init(&iterator, hdr6); hdr_iterator_last(&iterator); switch (iterator.hdr_type) { case IPPROTO_UDP: if (!ipv6_udp(hdr6, iterator.data, tuple)) return false; break; case IPPROTO_TCP: if (!ipv6_tcp(hdr6, iterator.data, tuple)) return false; break; case IPPROTO_ICMPV6: icmp6 = iterator.data; if (is_icmp6_info(icmp6->icmp6_type)) { if (!ipv6_icmp_info(hdr6, icmp6, tuple)) return false; } else { if (!ipv6_icmp_err(hdr6, icmp6, tuple)) return false; } break; default: log_info("Unsupported transport protocol for IPv6: %d.", iterator.hdr_type); icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); return false; } break; default: log_info("Packet's protocol (%d) is not IPv4 or IPv6.", be16_to_cpu(skb->protocol)); return false; } log_tuple(tuple); log_debug("Done step 1."); return true; }
verdict compute_out_tuple(struct tuple *in, struct tuple *out, struct packet *pkt_in) { struct session_entry *session; int error; log_debug("Step 3: Computing the Outgoing Tuple"); error = sessiondb_get(in, &session); if (error) { /* * Bogus ICMP errors might cause this because Filtering never cares for them, * so it's not critical. */ log_debug("Error code %d while trying to find the packet's session entry.", error); inc_stats(pkt_in, IPSTATS_MIB_INNOROUTES); return VERDICT_DROP; } /* * Though the end result is the same, the following section of code collides with the RFC * in a superfluous sense. * * If the IPv6 pool has multiple prefixes, algorithmically generating addresses at this point * is pointless because, in order to do that, we'd need to know which prefix was used when the * session was created. This bit of information would have to be extracted from the session. * However, the address already algorithmically generated also belongs to the session. * So why bother generating it again? Just copy it. * * Additionally, the RFC wants some information extracted from the BIB entry. * We *also* extract that information from the session because it's the same, by definition. * * And finally, the RFC wants some information extracted from the tuple. * Same deal. If you draw all the scenarios (weirdass ICMP errors included), it's always the * same as the session. * * Given all of that, I really don't understand why the RFC bothers with any of this, including * making a distinction between 3-tuples and 5-tuples. The outgoing tuple is always a copy of * the other side of the session, plain and simple. When you think about it, that last claim * makes sense even in a general sense. */ switch (in->l3_proto) { case L3PROTO_IPV6: out->l3_proto = L3PROTO_IPV4; out->l4_proto = in->l4_proto; out->src.addr4 = session->local4; out->dst.addr4 = session->remote4; break; case L3PROTO_IPV4: out->l3_proto = L3PROTO_IPV6; out->l4_proto = in->l4_proto; out->src.addr6 = session->local6; out->dst.addr6 = session->remote6; break; } session_return(session); log_tuple(out); log_debug("Done step 3."); return VERDICT_CONTINUE; }
/** * Extracts relevant data from "skb" and stores it in the "tuple" tuple. * * @param skb packet the data will be extracted from. * @param tuple this function will populate this value using "skb"'s contents. * @return whether packet processing should continue. */ verdict determine_in_tuple(struct sk_buff *skb, struct tuple *in_tuple) { struct icmphdr *icmp4; struct icmp6hdr *icmp6; verdict result = VER_CONTINUE; log_debug("Step 1: Determining the Incoming Tuple"); switch (skb_l3_proto(skb)) { case L3PROTO_IPV4: switch (skb_l4_proto(skb)) { case L4PROTO_UDP: result = ipv4_udp(skb, in_tuple); break; case L4PROTO_TCP: result = ipv4_tcp(skb, in_tuple); break; case L4PROTO_ICMP: icmp4 = icmp_hdr(skb); if (is_icmp4_info(icmp4->type)) { result = ipv4_icmp_info(skb, in_tuple); } else if (is_icmp4_error(icmp4->type)) { result = ipv4_icmp_err(skb, in_tuple); } else { log_debug("Unknown ICMPv4 type: %u. Dropping packet...", icmp4->type); inc_stats(skb, IPSTATS_MIB_INHDRERRORS); result = VER_DROP; } break; } break; case L3PROTO_IPV6: switch (skb_l4_proto(skb)) { case L4PROTO_UDP: result = ipv6_udp(skb, in_tuple); break; case L4PROTO_TCP: result = ipv6_tcp(skb, in_tuple); break; case L4PROTO_ICMP: icmp6 = icmp6_hdr(skb); if (is_icmp6_info(icmp6->icmp6_type)) { result = ipv6_icmp_info(skb, in_tuple); } else if (is_icmp6_error(icmp6->icmp6_type)) { result = ipv6_icmp_err(skb, in_tuple); } else { log_debug("Unknown ICMPv6 type: %u. Dropping packet...", icmp6->icmp6_type); inc_stats(skb, IPSTATS_MIB_INHDRERRORS); result = VER_DROP; } break; } break; } /* * We moved the transport-protocol-not-recognized ICMP errors to packet.c because they're * covered in validations. */ log_tuple(in_tuple); log_debug("Done step 1."); return result; }
/** * Extracts relevant data from "frag" and stores it in the "tuple" tuple. * * @param frag fragment the data will be extracted from. Whether the packet is fragmented or not, * this has to be the chunk whose fragment offset is zero. * @param tuple this function will populate this value using "frag"'s contents. * @return whether packet processing should continue. */ verdict determine_in_tuple(struct fragment *frag, struct tuple *tuple) { struct iphdr *hdr4; struct ipv6hdr *hdr6; struct icmphdr *icmp4; struct icmp6hdr *icmp6; verdict result = VER_CONTINUE; log_debug("Step 1: Determining the Incoming Tuple"); switch (frag->l3_hdr.proto) { case L3PROTO_IPV4: hdr4 = frag_get_ipv4_hdr(frag); switch (frag->l4_hdr.proto) { case L4PROTO_UDP: result = ipv4_udp(hdr4, frag_get_udp_hdr(frag), tuple); break; case L4PROTO_TCP: result = ipv4_tcp(hdr4, frag_get_tcp_hdr(frag), tuple); break; case L4PROTO_ICMP: icmp4 = frag_get_icmp4_hdr(frag); if (is_icmp4_info(icmp4->type)) { result = ipv4_icmp_info(hdr4, icmp4, tuple); } else if (is_icmp4_error(icmp4->type)) { result = ipv4_icmp_err(hdr4, icmp4, tuple); } else { log_warning("Unknown ICMPv4 type: %u. Dropping packet...", icmp4->type); result = VER_DROP; } break; case L4PROTO_NONE: log_crit(ERR_ILLEGAL_NONE, "IPv4 - First fragment has no transport header."); result = VER_DROP; } break; case L3PROTO_IPV6: hdr6 = frag_get_ipv6_hdr(frag); switch (frag->l4_hdr.proto) { case L4PROTO_UDP: result = ipv6_udp(hdr6, frag_get_udp_hdr(frag), tuple); break; case L4PROTO_TCP: result = ipv6_tcp(hdr6, frag_get_tcp_hdr(frag), tuple); break; case L4PROTO_ICMP: icmp6 = frag_get_icmp6_hdr(frag); if (is_icmp6_info(icmp6->icmp6_type)) { result = ipv6_icmp_info(hdr6, icmp6, tuple); } else if (is_icmp6_error(icmp6->icmp6_type)) { result = ipv6_icmp_err(hdr6, icmp6, tuple); } else { log_warning("Unknown ICMPv6 type: %u. Dropping packet...", icmp6->icmp6_type); result = VER_DROP; } break; case L4PROTO_NONE: log_crit(ERR_ILLEGAL_NONE, "IPv6 - First fragment has no transport header."); result = VER_DROP; } break; } /* * We moved the transport-protocol-not-recognized ICMP errors to fragment_db because they're * covered in validations. */ log_tuple(tuple); log_debug("Done step 1."); return result; }