static verdict ipv6_icmp_err(struct sk_buff *skb, struct tuple *tuple6) { struct ipv6hdr *inner_ipv6 = (struct ipv6hdr *) (icmp6_hdr(skb) + 1); struct hdr_iterator iterator = HDR_ITERATOR_INIT(inner_ipv6); struct udphdr *inner_udp; struct tcphdr *inner_tcp; struct icmp6hdr *inner_icmp; tuple6->src.addr6.l3 = inner_ipv6->daddr; tuple6->dst.addr6.l3 = inner_ipv6->saddr; hdr_iterator_last(&iterator); switch (iterator.hdr_type) { case NEXTHDR_UDP: inner_udp = iterator.data; tuple6->src.addr6.l4 = be16_to_cpu(inner_udp->dest); tuple6->dst.addr6.l4 = be16_to_cpu(inner_udp->source); tuple6->l4_proto = L4PROTO_UDP; break; case NEXTHDR_TCP: inner_tcp = iterator.data; tuple6->src.addr6.l4 = be16_to_cpu(inner_tcp->dest); tuple6->dst.addr6.l4 = be16_to_cpu(inner_tcp->source); tuple6->l4_proto = L4PROTO_TCP; break; case NEXTHDR_ICMP: inner_icmp = iterator.data; if (is_icmp6_error(inner_icmp->icmp6_type)) { log_debug("Packet is a ICMP error containing a ICMP error."); inc_stats(skb, IPSTATS_MIB_INHDRERRORS); return VER_DROP; } tuple6->src.addr6.l4 = be16_to_cpu(inner_icmp->icmp6_dataun.u_echo.identifier); tuple6->dst.addr6.l4 = tuple6->src.addr6.l4; tuple6->l4_proto = L4PROTO_ICMP; break; default: log_debug("Packet's inner packet is not UDP, TCP or ICMPv6 (%d).", iterator.hdr_type); inc_stats(skb, IPSTATS_MIB_INUNKNOWNPROTOS); return VER_DROP; } tuple6->l3_proto = L3PROTO_IPV6; return VER_CONTINUE; }
static verdict ipv6_icmp_err(struct ipv6hdr *hdr_ipv6, struct icmp6hdr *hdr_icmp, struct tuple *tuple) { struct ipv6hdr *inner_ipv6 = (struct ipv6hdr *) (hdr_icmp + 1); struct hdr_iterator iterator = HDR_ITERATOR_INIT(inner_ipv6); struct udphdr *inner_udp; struct tcphdr *inner_tcp; struct icmp6hdr *inner_icmp; tuple->src.addr.ipv6 = inner_ipv6->daddr; tuple->dst.addr.ipv6 = inner_ipv6->saddr; hdr_iterator_last(&iterator); switch (iterator.hdr_type) { case NEXTHDR_UDP: inner_udp = iterator.data; tuple->src.l4_id = be16_to_cpu(inner_udp->dest); tuple->dst.l4_id = be16_to_cpu(inner_udp->source); tuple->l4_proto = L4PROTO_UDP; break; case NEXTHDR_TCP: inner_tcp = iterator.data; tuple->src.l4_id = be16_to_cpu(inner_tcp->dest); tuple->dst.l4_id = be16_to_cpu(inner_tcp->source); tuple->l4_proto = L4PROTO_TCP; break; case NEXTHDR_ICMP: inner_icmp = iterator.data; if (is_icmp6_error(inner_icmp->icmp6_type)) { log_debug("Packet is a ICMP error containing a ICMP error."); return VER_DROP; } tuple->src.l4_id = be16_to_cpu(inner_icmp->icmp6_dataun.u_echo.identifier); tuple->dst.l4_id = tuple->src.l4_id; tuple->l4_proto = L4PROTO_ICMP; break; default: log_warning("Packet's inner packet is not UDP, TCP or ICMPv6 (%d).", iterator.hdr_type); return VER_DROP; } tuple->l3_proto = L3PROTO_IPV6; return VER_CONTINUE; }
static bool ipv6_validate_packet_len(struct sk_buff *skb_in, struct sk_buff *skb_out) { struct ipv6hdr *ip6_hdr = ipv6_hdr(skb_out); struct hdr_iterator iterator = HDR_ITERATOR_INIT(ip6_hdr); unsigned int ipv6_mtu; unsigned int ipv4_mtu; if (skb_out->len <= skb_out->dev->mtu) return true; hdr_iterator_last(&iterator); if (iterator.hdr_type == IPPROTO_ICMPV6) { struct icmp6hdr *icmpv6_hdr = icmp6_hdr(skb_out); if (is_icmp6_error(icmpv6_hdr->icmp6_type)) { int new_packet_len = skb_out->dev->mtu; int l3_payload_len = new_packet_len - (iterator.data - (void *) ip6_hdr); skb_trim(skb_out, new_packet_len); ip6_hdr->payload_len = cpu_to_be16(l3_payload_len); icmpv6_hdr->icmp6_cksum = 0; icmpv6_hdr->icmp6_cksum = csum_ipv6_magic(&ip6_hdr->saddr, &ip6_hdr->daddr, l3_payload_len, IPPROTO_ICMPV6, csum_partial(icmpv6_hdr, l3_payload_len, 0)); return true; } } ipv6_mtu = skb_out->dev->mtu; ipv4_mtu = skb_in->dev->mtu; log_debug("Packet is too large for the outgoing MTU and IPv6 routers don't do fragmentation. " "Dropping..."); icmp_send(skb_in, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, cpu_to_be32(min_uint(ipv6_mtu, ipv4_mtu + 20))); return false; }
static bool has_inner_pkt6(__u8 icmp6_type) { return is_icmp6_error(icmp6_type); }
/** * 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; }
/** * 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; }