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; }
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; }