static bool test_no_subheaders(void) { // Init. struct ipv6hdr *ip6_header; unsigned char *payload; ip6_header = kmalloc(sizeof(struct ipv6hdr) + 4, GFP_ATOMIC); if (!ip6_header) { log_warning("Unable to allocate a test header."); return false; } ip6_header->nexthdr = NEXTHDR_UDP; payload = (unsigned char *) (ip6_header + 1); // Test next function. { struct hdr_iterator iterator = HDR_ITERATOR_INIT(ip6_header); ASSERT_EQUALS_PTR(ip6_header, iterator.data, "First header, data"); ASSERT_EQUALS(-1, iterator.hdr_type, "First header, hdr type"); hdr_iterator_next(&iterator); ASSERT_EQUALS_PTR(payload, iterator.data, "Payload 1st, data"); ASSERT_EQUALS(NEXTHDR_UDP, iterator.hdr_type, "Payload 1st, hdr type"); hdr_iterator_next(&iterator); ASSERT_EQUALS_PTR(payload, iterator.data, "Payload 2nd, data"); ASSERT_EQUALS(NEXTHDR_UDP, iterator.hdr_type, "Payload 2nd, hdr type"); hdr_iterator_next(&iterator); hdr_iterator_next(&iterator); hdr_iterator_next(&iterator); ASSERT_EQUALS_PTR(payload, iterator.data, "Payload 3rd, data"); ASSERT_EQUALS(NEXTHDR_UDP, iterator.hdr_type, "Payload 3rd, hdr type"); } // Test last function. { struct hdr_iterator iterator = HDR_ITERATOR_INIT(ip6_header); hdr_iterator_last(&iterator); ASSERT_EQUALS_PTR(payload, iterator.data, "Last function, data"); ASSERT_EQUALS(NEXTHDR_UDP, iterator.hdr_type, "Last function, hdr type"); } // Test get extension header function. { void *frag_hdr_computed = get_extension_header(ip6_header, NEXTHDR_FRAGMENT); void *hop_by_hop_hdr_computed = get_extension_header(ip6_header, NEXTHDR_HOP); void *udp_hdr_computed = get_extension_header(ip6_header, NEXTHDR_UDP); ASSERT_EQUALS_PTR(NULL, frag_hdr_computed, "Get function, frag hdr"); ASSERT_EQUALS_PTR(NULL, hop_by_hop_hdr_computed, "Get function, hop-by-hop hdr"); // Cause the UDP header is not an extension header. ASSERT_EQUALS_PTR(NULL, udp_hdr_computed, "Get function, payload"); } return true; }
/** * Initializes both "pipeline" and "in" using the data from "tuple", "skb", and the assumption that * we're translating from 6 to 4. * "pipeline" defines the sequence of functions that will be executed later and "in" is basically a * summary of "skb". */ static bool init_pipeline_ipv6(struct pipeline *pipeline, struct packet_in *in, struct nf_conntrack_tuple *tuple, struct sk_buff *skb) { struct ipv6hdr *ip6_hdr = ipv6_hdr(skb); struct hdr_iterator iterator = HDR_ITERATOR_INIT(ip6_hdr); pipeline->l3_hdr_function = create_ipv4_hdr; pipeline->create_skb_function = create_skb; pipeline->l3_post_function = post_ipv4; in->packet = skb; in->tuple = tuple; in->l3_hdr = ip6_hdr; in->l3_hdr_type = IPPROTO_IPV6; in->l3_hdr_len = skb_transport_header(skb) - skb_network_header(skb); in->l3_hdr_basic_len = sizeof(*ip6_hdr); in->compute_l3_hdr_len = compute_ipv6_hdr_len; hdr_iterator_last(&iterator); if (iterator.hdr_type == NEXTHDR_AUTH || iterator.hdr_type == NEXTHDR_ESP) { // RFC 6146 section 5.1. log_warning(" Incoming IPv6 packet has an Auth header or an ESP header. Cannot translate; " "will drop the packet."); return false; } in->l4_hdr_type = iterator.hdr_type; switch (in->l4_hdr_type) { case NEXTHDR_TCP: in->l4_hdr_len = tcp_hdrlen(skb); pipeline->l4_post_function = post_tcp_ipv4; pipeline->l4_hdr_and_payload_function = copy_l4_hdr_and_payload; break; case NEXTHDR_UDP: in->l4_hdr_len = sizeof(struct udphdr); pipeline->l4_hdr_and_payload_function = copy_l4_hdr_and_payload; pipeline->l4_post_function = post_udp_ipv4; break; case NEXTHDR_ICMP: in->l4_hdr_len = sizeof(struct icmp6hdr); pipeline->l4_hdr_and_payload_function = create_icmp4_hdr_and_payload; pipeline->l4_post_function = post_icmp4; break; default: log_warning(" Unsupported l4 protocol (%d). Cannot translate.", in->l4_hdr_type); return false; } in->payload = iterator.data + in->l4_hdr_len; in->payload_len = be16_to_cpu(ip6_hdr->payload_len) // - (in->l3_hdr_len - sizeof(*ip6_hdr)) // - in->l4_hdr_len; return true; }
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 bool 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 IPPROTO_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); break; case IPPROTO_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); break; case IPPROTO_ICMPV6: /* We're not supporting ICMP inside ICMP yet. */ return false; /* inner_icmp = iterator.data; if (is_icmp6_error(inner_icmp->icmp6_type)) return false; tuple->src.l4_id = be16_to_cpu(inner_icmp->icmp6_dataun.u_echo.identifier); tuple->dst.l4_id = tuple->src.l4_id; break; */ default: log_warning("Packet's inner packet is not UDP, TCP or ICMPv6 (%d).", iterator.hdr_type); return false; } tuple->l3_proto = PF_INET6; tuple->l4_proto = iterator.hdr_type; return true; }
void *get_extension_header(struct ipv6hdr *ip6_hdr, __u8 hdr_id) { struct hdr_iterator iterator = HDR_ITERATOR_INIT(ip6_hdr); if (!is_extension_hdr(hdr_id)) return NULL; do { if (iterator.hdr_type == hdr_id) return iterator.data; } while (hdr_iterator_next(&iterator) == HDR_ITERATOR_SUCCESS); return NULL; }
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; }
/* * Size includes fragment header if packet is IPv6. */ static int net_hdr_size(void *pkt) { struct hdr_iterator iterator = HDR_ITERATOR_INIT((struct ipv6hdr *) pkt); struct iphdr *hdr4 = pkt; switch (get_l3_proto(pkt)) { case 6: hdr_iterator_last(&iterator); return iterator.data - pkt; case 4: return (hdr4->ihl << 2); default: log_err("Invalid mode: %u", get_l3_proto(pkt)); return -EINVAL; } }
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 test_subheaders(void) { // Init. const __u16 HOP_BY_HOP_HDR_LEN = 32; const __u16 ROUTING_HDR_LEN = 40; struct ipv6hdr *ip6_header; struct frag_hdr *fragment_hdr; struct ipv6_opt_hdr *hop_by_hop_hdr; struct ipv6_opt_hdr *routing_hdr; unsigned char *payload; ip6_header = kmalloc(sizeof(struct ipv6hdr) + sizeof(struct frag_hdr) + HOP_BY_HOP_HDR_LEN + ROUTING_HDR_LEN + 4, // (payload.) GFP_ATOMIC); if (!ip6_header) { log_warning("Unable to allocate a test header."); return false; } ip6_header->nexthdr = NEXTHDR_FRAGMENT; fragment_hdr = (struct frag_hdr *) (ip6_header + 1); fragment_hdr->nexthdr = NEXTHDR_HOP; hop_by_hop_hdr = (struct ipv6_opt_hdr *) (fragment_hdr + 1); hop_by_hop_hdr->nexthdr = NEXTHDR_ROUTING; hop_by_hop_hdr->hdrlen = (HOP_BY_HOP_HDR_LEN / 8) - 1; routing_hdr = ((void *) hop_by_hop_hdr) + HOP_BY_HOP_HDR_LEN; routing_hdr->nexthdr = NEXTHDR_UDP; routing_hdr->hdrlen = (ROUTING_HDR_LEN / 8) - 1; payload = ((void *) routing_hdr) + ROUTING_HDR_LEN; // Test next function. { struct hdr_iterator next_iterator = HDR_ITERATOR_INIT(ip6_header); ASSERT_EQUALS_PTR(ip6_header, next_iterator.data, "First (main) header, data"); ASSERT_EQUALS(-1, next_iterator.hdr_type, "First (main) header, hdr type"); hdr_iterator_next(&next_iterator); ASSERT_EQUALS_PTR(fragment_hdr, next_iterator.data, "Second (frag) header, data"); ASSERT_EQUALS(NEXTHDR_FRAGMENT, next_iterator.hdr_type, "Second (frag) header, hdr type"); hdr_iterator_next(&next_iterator); ASSERT_EQUALS_PTR(hop_by_hop_hdr, next_iterator.data, "Third (hop-by-hop) header, data"); ASSERT_EQUALS(NEXTHDR_HOP, next_iterator.hdr_type, "Third (hop-by-hop) header, hdr type"); hdr_iterator_next(&next_iterator); ASSERT_EQUALS_PTR(routing_hdr, next_iterator.data, "Fourth (Routing) header, data"); ASSERT_EQUALS(NEXTHDR_ROUTING, next_iterator.hdr_type, "Fourth (Routing) header, hdr type"); hdr_iterator_next(&next_iterator); ASSERT_EQUALS_PTR(payload, next_iterator.data, "Payload 1st, data"); ASSERT_EQUALS(NEXTHDR_UDP, next_iterator.hdr_type, "Payload 1st, hdr type"); hdr_iterator_next(&next_iterator); ASSERT_EQUALS_PTR(payload, next_iterator.data, "Payload 2nd, data"); ASSERT_EQUALS(NEXTHDR_UDP, next_iterator.hdr_type, "Payload 2nd, hdr type"); } // Test last function. { struct hdr_iterator last_iterator = HDR_ITERATOR_INIT(ip6_header); hdr_iterator_init(&last_iterator, ip6_header); hdr_iterator_last(&last_iterator); ASSERT_EQUALS_PTR(payload, last_iterator.data, "Last function, data"); ASSERT_EQUALS(NEXTHDR_UDP, last_iterator.hdr_type, "Last function, hdr type"); } // Test get extension header function. { void *frag_hdr_computed = get_extension_header(ip6_header, NEXTHDR_FRAGMENT); void *hop_by_hop_hdr_computed = get_extension_header(ip6_header, NEXTHDR_HOP); void *udp_hdr_computed = get_extension_header(ip6_header, NEXTHDR_UDP); ASSERT_EQUALS_PTR(fragment_hdr, frag_hdr_computed, "Get function, frag hdr"); ASSERT_EQUALS_PTR(hop_by_hop_hdr, hop_by_hop_hdr_computed, "Get function, hop-by-hop hdr"); // Cause the UDP header is not an extension header. ASSERT_EQUALS_PTR(NULL, udp_hdr_computed, "Get function, payload"); } return true; }
void hdr_iterator_init(struct hdr_iterator *iterator, struct ipv6hdr *main_hdr) { struct hdr_iterator defaults = HDR_ITERATOR_INIT(main_hdr); memcpy(iterator, &defaults, sizeof(defaults)); }
/** * Assumes that "l3_hdr" points to a ipv6hdr, and returns its size, extension headers included. */ static __u16 compute_ipv6_hdr_len(void *l3_hdr) { struct hdr_iterator iterator = HDR_ITERATOR_INIT((struct ipv6hdr *) l3_hdr); hdr_iterator_last(&iterator); return iterator.data - l3_hdr; }