https://github.com/cilium/cilium
Raw File
Tip revision: dcda3fd98d6cc748561c334ace996da8195c2218 authored by André Martins on 10 November 2020, 12:24:55 UTC
Prepare for release v1.9.0
Tip revision: dcda3fd
policy.h
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2016-2020 Authors of Cilium */

#ifndef __LIB_POLICY_H_
#define __LIB_POLICY_H_

#include <linux/icmp.h>

#include "drop.h"
#include "dbg.h"
#include "eps.h"
#include "maps.h"

#ifdef SOCKMAP
static __always_inline int
policy_sk_egress(__u32 identity, __u32 ip,  __u16 dport)
{
	void *map = lookup_ip4_endpoint_policy_map(ip);
	int dir = CT_EGRESS;
	__u8 proto = IPPROTO_TCP;
	struct policy_entry *policy;
	struct policy_key key = {
		.sec_label = identity,
		.dport = dport,
		.protocol = proto,
		.egress = !dir,
		.pad = 0,
	};

	if (!map)
		return CTX_ACT_OK;

	/* Start with L3/L4 lookup. */
	policy = map_lookup_elem(map, &key);
	if (likely(policy)) {
		/* FIXME: Need byte counter */
		__sync_fetch_and_add(&policy->packets, 1);
		if (unlikely(policy->deny))
			return DROP_POLICY_DENY;
		return policy->proxy_port;
	}

	/* L4-only lookup. */
	key.sec_label = 0;
	policy = map_lookup_elem(map, &key);
	if (likely(policy)) {
		/* FIXME: Need byte counter */
		__sync_fetch_and_add(&policy->packets, 1);
		if (unlikely(policy->deny))
			return DROP_POLICY_DENY;
		return policy->proxy_port;
	}
	key.sec_label = identity;

	/* If L4 policy check misses, fall back to L3. */
	key.dport = 0;
	key.protocol = 0;
	policy = map_lookup_elem(map, &key);
	if (likely(policy)) {
		/* FIXME: Need byte counter */
		__sync_fetch_and_add(&policy->packets, 1);
		if (unlikely(policy->deny))
			return DROP_POLICY_DENY;
		return CTX_ACT_OK;
	}

	/* Final fallback if allow-all policy is in place. */
	key.sec_label = 0;
	policy = map_lookup_elem(map, &key);
	if (likely(policy)) {
		/* FIXME: Need byte counter */
		__sync_fetch_and_add(&policy->packets, 1);
		if (unlikely(policy->deny))
			return DROP_POLICY_DENY;
		return CTX_ACT_OK;
	}

	return DROP_POLICY;
}
#else
static __always_inline void
account(struct __ctx_buff *ctx, struct policy_entry *policy)
{
	/* FIXME: Use per cpu counters */
	__sync_fetch_and_add(&policy->packets, 1);
	__sync_fetch_and_add(&policy->bytes, ctx_full_len(ctx));
}

static __always_inline int
__policy_can_access(const void *map, struct __ctx_buff *ctx, __u32 localID,
		    __u32 remoteID, __u16 dport, __u8 proto, int dir,
		    bool is_untracked_fragment, __u8 *match_type)
{
	struct policy_entry *policy;
	struct policy_key key = {
		.sec_label = remoteID,
		.dport = dport,
		.protocol = proto,
		.egress = !dir,
		.pad = 0,
	};

#ifdef ALLOW_ICMP_FRAG_NEEDED
	/* When ALLOW_ICMP_FRAG_NEEDED is defined we allow all packets
	 * of ICMP type 3 code 4 - Fragmentation Needed.
	 */
	if (proto == IPPROTO_ICMP) {
		void *data, *data_end;
		struct icmphdr icmphdr __align_stack_8;
		struct iphdr *ip4;
		__u32 off;

		if (!revalidate_data(ctx, &data, &data_end, &ip4))
			return DROP_INVALID;

		off = ((void *)ip4 - data) + ipv4_hdrlen(ip4);
		if (ctx_load_bytes(ctx, off, &icmphdr, sizeof(icmphdr)) < 0)
			return DROP_INVALID;

		if (icmphdr.type == ICMP_DEST_UNREACH &&
		    icmphdr.code == ICMP_FRAG_NEEDED)
			return CTX_ACT_OK;
	}
#endif /* ALLOW_ICMP_FRAG_NEEDED */

	/* L4 lookup can't be done on untracked fragments. */
	if (!is_untracked_fragment) {
		/* Start with L3/L4 lookup. */
		policy = map_lookup_elem(map, &key);
		if (likely(policy)) {
			cilium_dbg3(ctx, DBG_L4_CREATE, remoteID, localID,
				    dport << 16 | proto);

			account(ctx, policy);
			*match_type = POLICY_MATCH_L3_L4;
			if (unlikely(policy->deny))
				return DROP_POLICY_DENY;
			return policy->proxy_port;
		}

		/* L4-only lookup. */
		key.sec_label = 0;
		policy = map_lookup_elem(map, &key);
		if (likely(policy)) {
			account(ctx, policy);
			*match_type = POLICY_MATCH_L4_ONLY;
			if (unlikely(policy->deny))
				return DROP_POLICY_DENY;
			return policy->proxy_port;
		}
		key.sec_label = remoteID;
	}

	/* If L4 policy check misses, fall back to L3. */
	key.dport = 0;
	key.protocol = 0;
	policy = map_lookup_elem(map, &key);
	if (likely(policy)) {
		account(ctx, policy);
		*match_type = POLICY_MATCH_L3_ONLY;
		if (unlikely(policy->deny))
			return DROP_POLICY_DENY;
		return CTX_ACT_OK;
	}

	/* Final fallback if allow-all policy is in place. */
	key.sec_label = 0;
	policy = map_lookup_elem(map, &key);
	if (policy) {
		account(ctx, policy);
		*match_type = POLICY_MATCH_ALL;
		if (unlikely(policy->deny))
			return DROP_POLICY_DENY;
		return CTX_ACT_OK;
	}

	if (ctx_load_meta(ctx, CB_POLICY))
		return CTX_ACT_OK;

	if (is_untracked_fragment)
		return DROP_FRAG_NOSUPPORT;

	return DROP_POLICY;
}

/**
 * Determine whether the policy allows this traffic on ingress.
 * @arg ctx		Packet to allow or deny
 * @arg srcID		Source security identity for this packet
 * @arg dstID		Destination security identity for this packet
 * @arg dport		Destination port of this packet
 * @arg proto		L3 Protocol of this packet
 * @arg is_untracked_fragment	True if packet is a TCP/UDP datagram fragment
 *				AND IPv4 fragment tracking is disabled
 * @arg match_type		Pointer to store layers used for policy match
 *
 * Returns:
 *   - Positive integer indicating the proxy_port to handle this traffic
 *   - CTX_ACT_OK if the policy allows this traffic based only on labels/L3/L4
 *   - Negative error code if the packet should be dropped
 */
static __always_inline int
policy_can_access_ingress(struct __ctx_buff *ctx, __u32 srcID, __u32 dstID,
			  __u16 dport, __u8 proto, bool is_untracked_fragment,
			  __u8 *match_type, __u8 *audited)
{
	int ret;

	ret = __policy_can_access(&POLICY_MAP, ctx, dstID, srcID, dport,
				  proto, CT_INGRESS, is_untracked_fragment,
				  match_type);
	if (ret >= CTX_ACT_OK)
		return ret;

	cilium_dbg(ctx, DBG_POLICY_DENIED, srcID, dstID);

	*audited = 0;
#ifdef POLICY_AUDIT_MODE
	if (IS_ERR(ret)) {
		ret = CTX_ACT_OK;
		*audited = 1;
	}
#endif

	return ret;
}

#ifdef ENCAP_IFINDEX
static __always_inline bool is_encap(__u16 dport, __u8 proto)
{
	return proto == IPPROTO_UDP &&
		(dport == bpf_htons(PORT_UDP_VXLAN) ||
		 dport == bpf_htons(PORT_UDP_GENEVE) ||
		 dport == bpf_htons(PORT_UDP_VXLAN_LINUX));
}
#endif

static __always_inline int
policy_can_egress(struct __ctx_buff *ctx, __u32 srcID, __u32 dstID,
		  __u16 dport, __u8 proto, __u8 *match_type, __u8 *audited)
{
	int ret;

#ifdef ENCAP_IFINDEX
	if (srcID != HOST_ID && is_encap(dport, proto))
		return DROP_ENCAP_PROHIBITED;
#endif
	ret = __policy_can_access(&POLICY_MAP, ctx, srcID, dstID, dport, proto,
				  CT_EGRESS, false, match_type);
	if (ret >= 0)
		return ret;
	cilium_dbg(ctx, DBG_POLICY_DENIED, srcID, dstID);
	*audited = 0;
#ifdef POLICY_AUDIT_MODE
	if (IS_ERR(ret)) {
		ret = CTX_ACT_OK;
		*audited = 1;
	}
#endif
	return ret;
}

static __always_inline int policy_can_egress6(struct __ctx_buff *ctx,
					      const struct ipv6_ct_tuple *tuple,
					      __u32 srcID, __u32 dstID,
					      __u8 *match_type, __u8 *audited)
{
	return policy_can_egress(ctx, srcID, dstID, tuple->dport,
				 tuple->nexthdr, match_type, audited);
}

static __always_inline int policy_can_egress4(struct __ctx_buff *ctx,
					      const struct ipv4_ct_tuple *tuple,
					      __u32 srcID, __u32 dstID,
					      __u8 *match_type, __u8 *audited)
{
	return policy_can_egress(ctx, srcID, dstID, tuple->dport,
				 tuple->nexthdr, match_type, audited);
}

/**
 * Mark ctx to skip policy enforcement
 * @arg ctx	packet
 *
 * Will cause the packet to ignore the policy enforcement layer and
 * be considered accepted despite of the policy outcome.
 */
static __always_inline void policy_mark_skip(struct __ctx_buff *ctx)
{
	ctx_store_meta(ctx, CB_POLICY, 1);
}

static __always_inline void policy_clear_mark(struct __ctx_buff *ctx)
{
	ctx_store_meta(ctx, CB_POLICY, 0);
}
#endif /* SOCKMAP */
#else
static __always_inline void policy_mark_skip(struct __ctx_buff *ctx)
{
}

static __always_inline void policy_clear_mark(struct __ctx_buff *ctx)
{
}
#endif
back to top