net/netfilter/nf_conntrack_proto_icmpv6.c

Source file repositories/reference/linux-study-clean/net/netfilter/nf_conntrack_proto_icmpv6.c

File Facts

System
Linux kernel
Corpus path
net/netfilter/nf_conntrack_proto_icmpv6.c
Extension
.c
Size
9958 bytes
Lines
363
Domain
Networking Core
Bucket
Sockets, Protocols, Packet Path, And Network Policy
Inferred role
Networking Core: implementation source
Status
source implementation candidate

Why This File Exists

Networking stack implementation surface: socket APIs, protocol dispatch, packet flow, routing, filtering, and network namespaces.

Dependency Surface

Detected Declarations

Annotated Snippet

if (type < 0 || type >= sizeof(valid_new) || !valid_new[type]) {
			/* Can't create a new ICMPv6 `conn' with this. */
			pr_debug("icmpv6: can't create new conn with type %u\n",
				 type + 128);
			nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple);
			return -NF_ACCEPT;
		}
	}

	if (!timeout)
		timeout = icmpv6_get_timeouts(nf_ct_net(ct));

	/* Do not immediately delete the connection after the first
	   successful reply to avoid excessive conntrackd traffic
	   and also to handle correctly ICMP echo reply duplicates. */
	nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);

	return NF_ACCEPT;
}


static void icmpv6_error_log(const struct sk_buff *skb,
			     const struct nf_hook_state *state,
			     const char *msg)
{
	nf_l4proto_log_invalid(skb, state, IPPROTO_ICMPV6, "%s", msg);
}

static noinline_for_stack int
nf_conntrack_icmpv6_redirect(struct nf_conn *tmpl, struct sk_buff *skb,
			     unsigned int dataoff,
			     const struct nf_hook_state *state)
{
	u8 hl = ipv6_hdr(skb)->hop_limit;
	union nf_inet_addr outer_daddr;
	union {
		struct nd_opt_hdr nd_opt;
		struct rd_msg rd_msg;
	} tmp;
	const struct nd_opt_hdr *nd_opt;
	const struct rd_msg *rd_msg;

	rd_msg = skb_header_pointer(skb, dataoff, sizeof(*rd_msg), &tmp.rd_msg);
	if (!rd_msg) {
		icmpv6_error_log(skb, state, "short redirect");
		return -NF_ACCEPT;
	}

	if (rd_msg->icmph.icmp6_code != 0)
		return NF_ACCEPT;

	if (hl != 255 || !(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
		icmpv6_error_log(skb, state, "invalid saddr or hoplimit for redirect");
		return -NF_ACCEPT;
	}

	dataoff += sizeof(*rd_msg);

	/* warning: rd_msg no longer usable after this call */
	nd_opt = skb_header_pointer(skb, dataoff, sizeof(*nd_opt), &tmp.nd_opt);
	if (!nd_opt || nd_opt->nd_opt_len == 0) {
		icmpv6_error_log(skb, state, "redirect without options");
		return -NF_ACCEPT;
	}

	/* We could call ndisc_parse_options(), but it would need
	 * skb_linearize() and a bit more work.
	 */
	if (nd_opt->nd_opt_type != ND_OPT_REDIRECT_HDR)
		return NF_ACCEPT;

	memcpy(&outer_daddr.ip6, &ipv6_hdr(skb)->daddr,
	       sizeof(outer_daddr.ip6));
	dataoff += 8;
	return nf_conntrack_inet_error(tmpl, skb, dataoff, state,
				       IPPROTO_ICMPV6, &outer_daddr);
}

int nf_conntrack_icmpv6_error(struct nf_conn *tmpl,
			      struct sk_buff *skb,
			      unsigned int dataoff,
			      const struct nf_hook_state *state)
{
	union nf_inet_addr outer_daddr;
	const struct icmp6hdr *icmp6h;
	struct icmp6hdr _ih;
	int type;

	icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih);
	if (icmp6h == NULL) {

Annotation

Implementation Notes