int ipsec6_input_check(struct sk_buff **skb, __u8 *nexthdr)
{
	int rtn = 0;
	struct inet6_skb_parm *opt = NULL;
	int result = IPSEC_ACTION_BYPASS;
	struct sa_index auth_sa_idx;
	struct sa_index esp_sa_idx;
	struct selector selector;
	struct ipsec_sp *policy = NULL;
	int addr_type = 0;
	struct ipv6hdr *hdr = (*skb)->nh.ipv6h;

	IPSEC6_DEBUG("called\n");
#ifdef CONFIG_IPSEC_DEBUG
	{
		char buf[64];
		IPSEC6_DEBUG("dst addr: %s\n", 
				in6_ntop( &hdr->daddr, buf));
		IPSEC6_DEBUG("src addr: %s\n", 
				in6_ntop( &hdr->saddr, buf));
		IPSEC6_DEBUG("hdr->payload_len is %d\n", ntohs(hdr->payload_len)); 
	}
#endif /* CONFIG_IPSEC_DEBUG */
	/* XXX */
	addr_type = ipv6_addr_type(&hdr->daddr);
	if (addr_type & IPV6_ADDR_MULTICAST) {
		IPSEC6_DEBUG("address type multicast skip!\n");
		goto finish;
	}
	
	if ( *nexthdr == NEXTHDR_UDP ) { /* IKE */
		if (pskb_may_pull(*skb, (*skb)->h.raw - (*skb)->data + sizeof(struct udphdr))) {
			if ((*skb)->h.uh->source == __constant_htons(500)
				 && (*skb)->h.uh->dest == __constant_htons(500)) {
					IPSEC6_DEBUG("received IKE packet. skip!\n");
					goto finish;
			}
		}
	}

	opt = (struct inet6_skb_parm*)((*skb)->cb);

	if (opt->auth) {
		sa_index_init(&auth_sa_idx);
		ipv6_addr_copy(&((struct sockaddr_in6 *)&auth_sa_idx.dst)->sin6_addr,
		       &(*skb)->nh.ipv6h->daddr);
		((struct sockaddr_in6 *)&auth_sa_idx.dst)->sin6_family = AF_INET6;
		auth_sa_idx.prefixlen_d = 128;
		auth_sa_idx.ipsec_proto = SADB_SATYPE_AH;
		auth_sa_idx.spi = ((struct ipv6_auth_hdr*)((*skb)->nh.raw + opt->auth))->spi;
		result |= IPSEC_ACTION_AUTH;
		opt->auth=0;
	}

	if (opt->espspi) {
		sa_index_init(&esp_sa_idx);
		ipv6_addr_copy(&((struct sockaddr_in6 *)&esp_sa_idx.dst)->sin6_addr,
		       &(*skb)->nh.ipv6h->daddr);
		((struct sockaddr_in6 *)&esp_sa_idx.dst)->sin6_family = AF_INET6;
		esp_sa_idx.prefixlen_d = 128;
		esp_sa_idx.ipsec_proto = SADB_SATYPE_ESP;
		esp_sa_idx.spi = opt->espspi;
		result |= IPSEC_ACTION_ESP;
		opt->espspi=0;
	}

	if (result&IPSEC_ACTION_DROP) {
		IPSEC6_DEBUG("result is drop.\n");
		rtn = -EINVAL;
		goto finish;
	}

	/* copy selector XXX port */
	memset(&selector, 0, sizeof(struct selector));
	
	switch(*nexthdr) {

#ifdef CONFIG_IPV6_IPSEC_TUNNEL
	case NEXTHDR_IPV6:
		IPSEC6_DEBUG("nexthdr: ipv6.\n");
		selector.mode = IPSEC_MODE_TUNNEL;
		break;
#endif
	case NEXTHDR_ICMP:
		IPSEC6_DEBUG("nexthdr: icmp.\n");
		selector.proto = IPPROTO_ICMPV6;
		break;

	case NEXTHDR_TCP:
		IPSEC6_DEBUG("nexthdr: tcp.\n");
		selector.proto = IPPROTO_TCP;
		break;

	case NEXTHDR_UDP:
		IPSEC6_DEBUG("nexthdr: udp.\n");
		selector.proto = IPPROTO_UDP;
		break;
	default:
		break;
	}

	IPSEC6_DEBUG("nexthdr = %u\n", *nexthdr);

#ifdef CONFIG_IPV6_IPSEC_TUNNEL
	if (selector.mode == IPSEC_MODE_TUNNEL) {
		struct ipv6hdr *h = NULL;
		if (pskb_may_pull(*skb, (*skb)->h.raw - (*skb)->data + sizeof(struct ipv6hdr))) {
			h = (struct ipv6hdr*) (*skb)->h.raw;

			((struct sockaddr_in6 *)&selector.src)->sin6_family = AF_INET6;
			ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.src)->sin6_addr,
				       &h->saddr);
			((struct sockaddr_in6 *)&selector.dst)->sin6_family = AF_INET6;
			ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.dst)->sin6_addr,
				       &h->daddr);
		} else {
			rtn = -EINVAL;
			goto finish;
		}
	} else {
#endif
		((struct sockaddr_in6 *)&selector.src)->sin6_family = AF_INET6;
		ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.src)->sin6_addr,
			       &(*skb)->nh.ipv6h->saddr);
		((struct sockaddr_in6 *)&selector.dst)->sin6_family = AF_INET6;
		ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.dst)->sin6_addr,
			       &(*skb)->nh.ipv6h->daddr);
#ifdef CONFIG_IPV6_IPSEC_TUNNEL
	}
#endif
	selector.prefixlen_d = 128;
	selector.prefixlen_s = 128;

	/* beggining of matching check selector and policy */
	IPSEC6_DEBUG("start match check SA and policy.\n");

#ifdef CONFIG_IPSEC_DEBUG
	{
		char buf[64];
		IPSEC6_DEBUG("selector dst addr: %s\n", 
			  in6_ntop( &((struct sockaddr_in6 *)&selector.dst)->sin6_addr, buf));
		IPSEC6_DEBUG("selector src addr: %s\n", 
			  in6_ntop( &((struct sockaddr_in6 *)&selector.src)->sin6_addr, buf));
	}
#endif /* CONFIG_IPSEC_DEBUG */
	policy = spd_get(&selector);
		
	if (policy) {

		read_lock_bh(&policy->lock);

		/* non-ipsec packet processing: If this packet doesn't
		 * have any IPSEC headers, then consult policy to see
		 * what to do with packet. If policy says to apply IPSEC,
		 * and there is an SA, then pass packet to netxt layer,
		 * if ther isn't an SA, then drop the packet.
		 */
		if (policy->policy_action == IPSEC_POLICY_DROP) {
			rtn = -EINVAL;
			read_unlock_bh(&policy->lock);
			goto finish;
		}

		if (policy->policy_action == IPSEC_POLICY_BYPASS) {
			rtn = 0;
			read_unlock_bh(&policy->lock);
			goto finish;
		}

		if (policy->policy_action == IPSEC_POLICY_APPLY) {
			if (result&IPSEC_ACTION_AUTH) {
				if (policy->auth_sa_idx) {
					if (sa_index_compare(&auth_sa_idx, policy->auth_sa_idx)) {
						rtn = -EINVAL;
					}
				} else {
					rtn = -EINVAL;
				}
			} else {
				if (policy->auth_sa_idx) rtn = -EINVAL;
			}

			if (result&IPSEC_ACTION_ESP) {
				if (policy->esp_sa_idx) {
					if (sa_index_compare(&esp_sa_idx, policy->esp_sa_idx)) {
						rtn = -EINVAL;
					}
				} else {
					rtn = -EINVAL;
				}
			} else {
				if (policy->esp_sa_idx) rtn = -EINVAL;
			}
		}

		read_unlock_bh(&policy->lock);
		ipsec_sp_put(policy);
	} else {
		if (!result) {
			rtn = 0;
		} else {
			IPSEC6_DEBUG("matching pair of SA and policy not found, through!\n"); 
			rtn = -EINVAL;
			goto finish;		
		}
	}


	IPSEC6_DEBUG("end match check SA and policy.\n");
	/* end of matching check selector and policy */
		

finish:

	return rtn;
}
コード例 #2
0
static int ipsec6_output_check_core(struct selector *selector, struct ipsec_sp **policy_ptr)
{
	int error = 0;
	struct ipsec_sp *policy = NULL;
	int result = IPSEC_ACTION_BYPASS; 	/* default */

	IPSEC6_DEBUG("called\n");

	if (!selector) {
		IPSEC6_DEBUG("selector is NULL\n");
		error = -EINVAL;
		goto err;
	}

	policy = spd_get(selector);
	if (!policy) { /* not match ! */
		IPSEC6_DEBUG("no policy exists.\n");
		result = IPSEC_ACTION_BYPASS;
		goto err;
	}

	read_lock(&policy->lock);
	if (policy->policy_action == IPSEC_POLICY_DROP) {
		result = IPSEC_ACTION_DROP;
		read_unlock(&policy->lock);
		goto err;
	}  else if (policy->policy_action == IPSEC_POLICY_BYPASS) {
		result = IPSEC_ACTION_BYPASS;
		read_unlock(&policy->lock);
		goto err;
	}
	
	/* policy must then be to apply ipsec */
	if (policy->auth_sa_idx) {
		if (policy->auth_sa_idx->sa) {
			struct ipsec_sa *sa = NULL;
			ipsec_sa_hold(policy->auth_sa_idx->sa);
			sa = policy->auth_sa_idx->sa;
			read_unlock(&policy->lock);

			read_lock(&sa->lock);
			switch (sa->state) {
			case SADB_SASTATE_MATURE:
			case SADB_SASTATE_DYING:
				result |= IPSEC_ACTION_AUTH;
				break;
			default:
				result = IPSEC_ACTION_DROP;
			}
			read_unlock(&sa->lock);
			ipsec_sa_put(sa);
		} else {
			/* copy sa_idx in policy to avoid to lock SADB and SPD at the same time */
			struct sa_index sa_idx;
			sa_index_init(&sa_idx);
			sa_index_copy(&sa_idx, policy->auth_sa_idx);
			read_unlock(&policy->lock);

			sa_idx.sa = sadb_find_by_sa_index(&sa_idx);
			if (sa_idx.sa) {
				write_lock(&policy->lock);
				policy->auth_sa_idx->sa = sa_idx.sa;
				ipsec_sa_hold(policy->auth_sa_idx->sa);
				write_unlock(&policy->lock);
				ipsec_sa_put(sa_idx.sa);
				result |= IPSEC_ACTION_AUTH;
			} else {
				/* SADB_ACQUIRE message should be thrown up to KMd */
				result = IPSEC_ACTION_DROP;
			}
		}
	} else {
		read_unlock(&policy->lock);
	}

	read_lock(&policy->lock);
	if (policy->esp_sa_idx) {
		if (policy->esp_sa_idx->sa) {
			struct ipsec_sa *sa = NULL;
			ipsec_sa_hold(policy->esp_sa_idx->sa);
			sa = policy->esp_sa_idx->sa;
			read_unlock(&policy->lock);

			read_lock(&sa->lock);
			switch (sa->state) {
			case SADB_SASTATE_MATURE:
			case SADB_SASTATE_DYING:
				result |= IPSEC_ACTION_ESP;
				break;
			default:
				result = IPSEC_ACTION_DROP;
			}
			read_unlock(&sa->lock);
			ipsec_sa_put(sa);
		} else {
			/* copy sa_idx in policy to avoid to lock SADB and SPD at the same time */
			struct sa_index sa_idx;
			sa_index_init(&sa_idx);
			sa_index_copy(&sa_idx, policy->esp_sa_idx);
			read_unlock(&policy->lock);

			sa_idx.sa = sadb_find_by_sa_index(&sa_idx);
			if (sa_idx.sa) {
				write_lock(&policy->lock);
				policy->esp_sa_idx->sa = sa_idx.sa;
				ipsec_sa_hold(policy->esp_sa_idx->sa);
				write_unlock(&policy->lock);
				ipsec_sa_put(sa_idx.sa);
				result |= IPSEC_ACTION_ESP;
			} else {
				/* SADB_ACQUIRE message should be thrown up to KMd */
				result = IPSEC_ACTION_DROP;
			}
		}
	} else {
		read_unlock(&policy->lock);
	}

	*policy_ptr= policy;

	IPSEC6_DEBUG("end\n");	

err:
	return result;
}