/* XXX must insert window size sa_replay_window member */ static int check_replay_window(struct sa_replay_window *rw, __u32 hdrseq) { __u32 diff; __u32 seq = ntohl(hdrseq); if (!sysctl_ipsec_replay_window) { IPSEC6_DEBUG("disable replay window check, skip!\n"); return 1; } IPSEC6_DEBUG("overflow: %u\n" " size: %u\n" " seq_num: %x\n" "last_seq: %x\n" " bitmap: %08x\n" " curr seq: %x\n", rw->overflow, rw->size, rw->seq_num, rw->last_seq, rw->bitmap, seq); if (seq == 0) { return 0; /* first == 0 or wrapped */ } if (seq > rw->last_seq) return 1; /* larger is good */ diff = rw->last_seq - seq; if (diff >= rw->size) { return 0; /* too old or wrapped */ } if ( rw->bitmap & ((u_long)1 << diff) ) { return 0; /* already seen */ } return 1; /* out of order but good */ }
static void update_replay_window(struct sa_replay_window *rw, __u32 hdrseq) { __u32 diff; __u32 seq = ntohl(hdrseq); if (!sysctl_ipsec_replay_window) { IPSEC6_DEBUG("disable replay window check, skip!\n"); return; } if (seq == 0) return; if (seq > rw->last_seq) { /* new larger sequence number */ diff = seq - rw->last_seq; if (diff < rw->size) { /* In window */ rw->bitmap <<= diff; rw->bitmap |= 1; /* set bit for this packet */ } else { rw->bitmap = 1; /* This packet has a "way larger" */ } rw->last_seq = seq; } diff = rw->last_seq - seq; rw->bitmap |= ((u_long)1 << diff); /* mark as seen */ }
void print_ip6packet(struct ip6_packet_refs refs) { char srcadr[50], dstadr[50], buf[1024], *s; int length; length = ntohs(refs.ipv6hdr->payload_len) + sizeof(struct ipv6hdr); in6_ntop(&refs.ipv6hdr->saddr, srcadr); in6_ntop(&refs.ipv6hdr->daddr, dstadr); sprintf(buf,"src: %s dest: %s total_length: %u", srcadr,dstadr,length); IPSEC6_DEBUG("IPv6 header (%s)\n",buf); if (refs.rthdr) { IPSEC6_DEBUG(" Routing header\n"); } if (refs.destopthdr) { IPSEC6_DEBUG(" Destination options header\n"); } if (refs.hopopthdr) { IPSEC6_DEBUG(" Hop-by-hop options header\n"); } if (refs.authhdr) { sprintf(buf,"nexthdr: %u len: %u, spi: 0x%x seq: 0x%x", refs.authhdr->nexthdr,refs.authhdr->hdrlen, ntohl(refs.authhdr->spi),ntohl(refs.authhdr->seq_no)); IPSEC6_DEBUG(" Authentication header (%s)\n",buf); } switch (refs.resttype) { case NEXTHDR_FRAGMENT: IPSEC6_DEBUG(" Fragment header\n"); break; case NEXTHDR_ICMP: switch (((struct icmp6hdr*)refs.rest)->icmp6_type) { case ICMPV6_DEST_UNREACH: s = "DEST_UNREACH"; break; case ICMPV6_PKT_TOOBIG: s = "PKT_TOOBIG"; break; case ICMPV6_TIME_EXCEED: s = "TIME_EXCEED"; break; case ICMPV6_PARAMPROB: s = "PARAMPROB"; break; case ICMPV6_ECHO_REQUEST: s = "ECHO_REQUEST"; break; case ICMPV6_ECHO_REPLY: s = "ECHO_REPLY"; break; case ICMPV6_MGM_QUERY: s = "MGM_QUERY"; break; case ICMPV6_MGM_REPORT: s = "MGM_REPORT"; break; case ICMPV6_MGM_REDUCTION: s = "MGM_REDUCTION"; break; default: s = "unknown"; } sprintf(buf,"type: %u (0x%x) - %s", ((struct icmp6hdr*)refs.rest)->icmp6_type, ((struct icmp6hdr*)refs.rest)->icmp6_type,s); IPSEC6_DEBUG(" ICMP header (%s)\n",buf); break; case NEXTHDR_UDP/*17*/: IPSEC6_DEBUG(" UDP packet\n"); break; case NEXTHDR_TCP/*6*/: IPSEC6_DEBUG(" TCP packet\n"); break; case NEXTHDR_ESP: sprintf(buf,"spi: %u seq: %u", ntohl(((struct ipv6_esp_hdr*)refs.rest)->spi), ntohl(((struct ipv6_esp_hdr*)refs.rest)->seq_no)); IPSEC6_DEBUG(" Encrypted security payload (%s)\n",buf); break; default: IPSEC6_DEBUG(" Unknown header type %u (0x%x)\n",refs.resttype, refs.resttype); } }
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; }
int ipsec6_input_check_esp(struct sk_buff **skb, struct ipv6_esp_hdr* esphdr, u8 *nexthdr) { int len = 0; int rtn = 0; struct sa_index sa_idx; u8 *authdata = NULL; u8 *srcdata = NULL; int srcsize = 0, totalsize = 0, hashsize = 0, encsize = 0; IPSEC6_DEBUG("start esp processing\n"); if (!(*skb&&esphdr)) { printk(KERN_ERR "ipsec6_input_check_esp: parameters are invalid\n"); goto finish; } if (skb_is_nonlinear(*skb)) { u16 offset = ((char*)esphdr) - (char*)((*skb)->nh.raw); if (!skb_linearize(*skb, GFP_ATOMIC)) { esphdr = (struct ipv6_esp_hdr*)((*skb)->nh.raw + offset); } else { printk(KERN_ERR "ipsec6_input_check_esp: counld not linearize skb\n"); rtn = -EINVAL; goto finish; } } /* Check SPI */ IPSEC6_DEBUG("esphdr->spi is 0x%x\n", ntohl(esphdr->spi)); sa_index_init(&sa_idx); ipv6_addr_copy(&((struct sockaddr_in6 *)&sa_idx.dst)->sin6_addr, &(*skb)->nh.ipv6h->daddr); ((struct sockaddr_in6 *)&sa_idx.dst)->sin6_family = AF_INET6; sa_idx.prefixlen_d = 128; sa_idx.ipsec_proto = SADB_SATYPE_ESP; sa_idx.spi = esphdr->spi; sa_idx.sa = sadb_find_by_sa_index(&sa_idx); if (!sa_idx.sa) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: not found SA for esp\n"); goto finish; } write_lock_bh(&sa_idx.sa->lock); IPSEC6_DEBUG("use kerneli version.\n"); if ( sa_idx.sa->esp_algo.algo == SADB_EALG_NONE ) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: not found encryption algorithm in SA!\n"); goto unlock_finish; } len = ntohs((*skb)->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr); if (len > (*skb)->len + ((char*)(*skb)->data - (char*)(*skb)->nh.ipv6h)) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: received packet length is wrong\n"); goto unlock_finish; } totalsize = len - ((((char*)esphdr) - ((char*)(*skb)->nh.ipv6h))); if (!(sa_idx.sa->esp_algo.cx->ci)) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: not found esp algo\n"); goto unlock_finish; } if ( !check_replay_window(&sa_idx.sa->replay_window, esphdr->seq_no) ) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: replay check err!\n"); kfree(srcdata); goto unlock_finish; } encsize = totalsize - sa_idx.sa->esp_algo.cx->ci->ivsize - 8; /* 8 = SPI + Sequence Number */ if ( sa_idx.sa->auth_algo.algo != SADB_AALG_NONE ) { /* Calculate size */ /* The tail of payload does not have to be aligned */ /* with a multiple number of 64 bit. */ /* 64 bit alignment is adapted to the position of top of header.*/ hashsize = sa_idx.sa->auth_algo.digest_len; encsize -= hashsize; authdata=kmalloc(sa_idx.sa->auth_algo.dx->di->blocksize, GFP_ATOMIC); sa_idx.sa->auth_algo.dx->di->hmac_atomic(sa_idx.sa->auth_algo.dx, sa_idx.sa->auth_algo.key, sa_idx.sa->auth_algo.key_len, (char*)esphdr, totalsize - hashsize, authdata); /* Originally, IABG uses "for" loop for matching authentication data. */ /* I change it into memcmp routine. */ if (memcmp(authdata, &((char*)esphdr)[totalsize - hashsize], sa_idx.sa->auth_algo.digest_len )) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: invalid checksum in ESP\n"); kfree(authdata); goto unlock_finish; } kfree(authdata); authdata = NULL; } /* Decrypt data */ srcdata = kmalloc(encsize, GFP_ATOMIC); if (!srcdata) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: can't allocate memory for decrypt\n"); goto unlock_finish; } IPSEC6_DEBUG("len=%d, totalsize=%d, encsize=%d\n", len, totalsize, encsize); if (!(sa_idx.sa->esp_algo.iv)) { /* first packet */ sa_idx.sa->esp_algo.iv = kmalloc(sa_idx.sa->esp_algo.cx->ci->ivsize, GFP_ATOMIC); } memcpy(sa_idx.sa->esp_algo.iv, esphdr->enc_data, sa_idx.sa->esp_algo.cx->ci->ivsize); sa_idx.sa->esp_algo.cx->ci->decrypt_atomic_iv(sa_idx.sa->esp_algo.cx, ((u8 *)(esphdr->enc_data)) + sa_idx.sa->esp_algo.cx->ci->ivsize, srcdata, encsize, sa_idx.sa->esp_algo.iv); /* encsize - (pad_len + next_hdr) - pad_len */ srcsize = encsize - 2 - srcdata[encsize-2]; IPSEC6_DEBUG("Original data is srcsize=%d, padlength=%d\n", srcsize, srcdata[encsize-2]); if (srcsize <= 0) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: Encrypted packet contains garbage(Size of decrypted packet < 0).\n"); kfree(srcdata); goto unlock_finish; } update_replay_window(&sa_idx.sa->replay_window, esphdr->seq_no); *nexthdr = srcdata[encsize-1]; IPSEC6_DEBUG("nexthdr = %u\n", *nexthdr); memcpy(esphdr, srcdata, srcsize); skb_trim(*skb, (*skb)->len + srcsize - totalsize); (*skb)->nh.ipv6h->payload_len = htons(((char *)esphdr - (char *)((*skb)->nh.ipv6h)) - sizeof(struct ipv6hdr) + srcsize); /* ok ? -mk */ kfree(srcdata); srcdata = NULL; rtn = sa_idx.spi; /* Otherwise checksum of fragmented udp packets fails (udp.c, csum_fold) */ (*skb)->ip_summed = CHECKSUM_UNNECESSARY; (*skb)->security |= RCV_CRYPT; if (!sa_idx.sa->fuse_time) { sa_idx.sa->fuse_time = jiffies; sa_idx.sa->lifetime_c.usetime = (sa_idx.sa->fuse_time) / HZ; ipsec_sa_mod_timer(sa_idx.sa); IPSEC6_DEBUG("set fuse_time = %lu\n", (sa_idx.sa->fuse_time)); } sa_idx.sa->lifetime_c.bytes += totalsize; IPSEC6_DEBUG("sa->bytes=%-9u %-9u\n", /* XXX: %-18Lu */ (__u32)((sa_idx.sa->lifetime_c.bytes) >> 32), (__u32)(sa_idx.sa->lifetime_c.bytes)); if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_s.bytes && sa_idx.sa->lifetime_s.bytes) { sa_idx.sa->state = SADB_SASTATE_DYING; IPSEC6_DEBUG("change sa state DYING\n"); } if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_h.bytes && sa_idx.sa->lifetime_h.bytes) { sa_idx.sa->state = SADB_SASTATE_DEAD; IPSEC6_DEBUG("change sa state DEAD\n"); } unlock_finish: write_unlock_bh(&sa_idx.sa->lock); /* unlock SA */ ipsec_sa_put(sa_idx.sa); finish: return rtn; }
int ipsec6_input_check_ah(struct sk_buff **skb, struct ipv6_auth_hdr *authhdr) { int rtn = 0; __u8* authdata; size_t authsize; char *packet; int offset; struct sa_index sa_idx; struct inet6_skb_parm opt; IPSEC6_DEBUG("start auth header processing\n"); if (!((*skb)&&authhdr)) { IPSEC6_DEBUG("parameters is invalid\n"); goto finish; } /* Check SPI */ IPSEC6_DEBUG("authhdr->spi is 0x%x\n", ntohl(authhdr->spi)); sa_index_init(&sa_idx); ipv6_addr_copy(&((struct sockaddr_in6 *)&sa_idx.dst)->sin6_addr, &(*skb)->nh.ipv6h->daddr); ((struct sockaddr_in6 *)&sa_idx.dst)->sin6_family = AF_INET6; sa_idx.prefixlen_d = 128; sa_idx.ipsec_proto = SADB_SATYPE_AH; sa_idx.spi = authhdr->spi; sa_idx.sa = sadb_find_by_sa_index(&sa_idx); if (!sa_idx.sa) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_ah: not found SA for ah\n"); goto finish; } write_lock_bh(&sa_idx.sa->lock); if (sa_idx.sa->auth_algo.algo == SADB_AALG_NONE ) { if (net_ratelimit()) printk(KERN_ERR "ipsec_input_calc_ah: not found auth algo.\n"); goto unlock_finish; } if (!check_replay_window(&sa_idx.sa->replay_window, authhdr->seq_no)) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_ah: replay check err!\n"); goto unlock_finish; } authsize = ntohs((*skb)->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr); if (authsize > (*skb)->len + ((char*)(*skb)->data - (char*)(*skb)->nh.ipv6h)) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_ah: the packet length is wrong\n"); goto unlock_finish; } packet = kmalloc(((authsize + 3) & ~3) + sa_idx.sa->auth_algo.dx->di->blocksize, GFP_ATOMIC); if (!packet) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_ah: can't get memory for pakcet\n"); goto unlock_finish; } authdata = packet + ((authsize + 3) & ~3); offset = (char *)((*skb)->nh.ipv6h) - (char *)((*skb)->data); if (skb_copy_bits(*skb, offset, packet, authsize)) { IPSEC6_DEBUG("packet copy failed\n"); goto unlock_finish; } memset(&opt, 0, sizeof(struct inet6_skb_parm)); ipsec6_input_get_offset(packet, authsize, &opt); zero_out_for_ah(&opt, packet); sa_idx.sa->auth_algo.dx->di->hmac_atomic(sa_idx.sa->auth_algo.dx, sa_idx.sa->auth_algo.key, sa_idx.sa->auth_algo.key_len, packet, authsize, authdata); /* Originally, IABG uses "for" loop for matching authentication data. */ /* I change it into memcmp routine. */ if (memcmp(authdata, authhdr->auth_data, sa_idx.sa->auth_algo.digest_len)) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_ah: invalid checksum in AH.\n"); kfree(packet); goto unlock_finish; } kfree(packet); rtn = 1; (*skb)->security |= RCV_AUTH; /* ? we must rewrite linux/ipsec.h */ update_replay_window(&sa_idx.sa->replay_window, authhdr->seq_no); if (!sa_idx.sa->fuse_time) { sa_idx.sa->fuse_time = jiffies; sa_idx.sa->lifetime_c.usetime = (sa_idx.sa->fuse_time) / HZ; ipsec_sa_mod_timer(sa_idx.sa); IPSEC6_DEBUG("set fuse_time = %lu\n", sa_idx.sa->fuse_time); } sa_idx.sa->lifetime_c.bytes += (*skb)->tail - (*skb)->head; IPSEC6_DEBUG("sa->lifetime_c.bytes=%-9u %-9u\n", /* XXX: %-18Lu */ (__u32)((sa_idx.sa->lifetime_c.bytes) >> 32), (__u32)(sa_idx.sa->lifetime_c.bytes)); if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_s.bytes && sa_idx.sa->lifetime_s.bytes) { sa_idx.sa->state = SADB_SASTATE_DYING; IPSEC6_DEBUG("change sa state DYING\n"); } if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_h.bytes && sa_idx.sa->lifetime_h.bytes) { sa_idx.sa->state = SADB_SASTATE_DEAD; IPSEC6_DEBUG("change sa state DEAD\n"); } unlock_finish: write_unlock_bh(&sa_idx.sa->lock); /* unlock SA */ ipsec_sa_put(sa_idx.sa); 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; }
int ipsec6_output_check(struct sock *sk, struct flowi *fl, struct ipsec_sp **policy_ptr) { struct in6_addr *saddr,*daddr; u16 sport,dport; unsigned char proto; struct selector selector; int result = IPSEC_ACTION_BYPASS; /* default */ IPSEC6_DEBUG("called\n"); if (!sk && !fl) { printk(KERN_ERR "flowi and sock are NULL\n"); result = -EINVAL; goto err; } if (fl && fl->fl6_src) { saddr = fl->fl6_src; } else { if (sk) { saddr = &sk->net_pinfo.af_inet6.saddr; } else { result = -EINVAL; printk(KERN_ERR "sock is null\n"); goto err; } } if (fl && fl->fl6_dst) { daddr = fl->fl6_dst; } else { if (sk) { daddr = &sk->net_pinfo.af_inet6.daddr; } else { result = -EINVAL; printk(KERN_ERR "flowi and sock are NULL\n"); goto err; } } if (fl) { sport=fl->uli_u.ports.sport; dport=fl->uli_u.ports.dport; proto=fl->proto; } else if (sk) { sport=sk->sport; dport=sk->dport; proto=sk->protocol; } else { result = -EINVAL; printk(KERN_ERR "flowi and sock are NULL\n"); goto err; } /* for ISKAMP see RFC2408 */ if (proto == IPPROTO_UDP && sport == __constant_htons(500) && dport == __constant_htons(500)) { result = IPSEC_ACTION_BYPASS; /* default */ goto err; } /* XXX have to decide to the policy of ICMP messages -mk*/ if (proto == IPPROTO_ICMPV6) { #ifdef CONFIG_APPLY_ICMPV6_IPSEC /* XXX currently we can't handle NS-NA -mk */ sport = 0; dport = 0; #else IPSEC6_DEBUG("skip ICMP packet!\n"); goto err; #endif /* CONFIG_APPLY_ICMPV6_IPSEC */ } /* XXX config port policy */ memset(&selector, 0, sizeof(struct selector)); ((struct sockaddr_in6 *)&selector.src)->sin6_family = AF_INET6; ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.src)->sin6_addr, saddr); ((struct sockaddr_in6 *)&selector.dst)->sin6_family = AF_INET6; ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.dst)->sin6_addr, daddr); selector.proto = proto; selector.prefixlen_d = 128; selector.prefixlen_s = 128; ((struct sockaddr_in6 *)&selector.src)->sin6_port = sport; ((struct sockaddr_in6 *)&selector.dst)->sin6_port = dport; #ifdef CONFIG_IPSEC_DEBUG { char buf[64]; IPSEC6_DEBUG("original dst addr: %s\n", in6_ntop(daddr, buf)); IPSEC6_DEBUG("original dst port: %u\n", ntohs(dport)); IPSEC6_DEBUG("original src addr: %s\n", in6_ntop(saddr, buf)); IPSEC6_DEBUG("original src port: %u\n", ntohs(sport)); IPSEC6_DEBUG("selector dst addr: %s\n", in6_ntop( &((struct sockaddr_in6 *)&selector.dst)->sin6_addr, buf)); IPSEC6_DEBUG("selector dst port: %u\n", ntohs(((struct sockaddr_in6 *)&selector.dst)->sin6_port)); IPSEC6_DEBUG("selector src addr: %s\n", in6_ntop( &((struct sockaddr_in6 *)&selector.src)->sin6_addr, buf)); IPSEC6_DEBUG("selector src port: %u\n", ntohs(((struct sockaddr_in6 *)&selector.src)->sin6_port)); IPSEC6_DEBUG("selector proto: %u\n", selector.proto); } #endif /* CONFIG_IPSEC_DEBUG */ result = ipsec6_output_check_core(&selector, policy_ptr); err: return result; }