/* * remove it from the hash chain, decrementing hash count */ void ipsec_sa_rm(struct ipsec_sa *ips) { unsigned int hashval; char sa[SATOT_BUF]; size_t sa_len; if(ips == NULL) { return; } hashval = IPS_HASH(&ips->ips_said); sa_len = KLIPS_SATOT(debug_xform, &ips->ips_said, 0, sa, sizeof(sa)); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "unhashing SA:%s (ref=%u), hashval=%d.\n", sa_len ? sa : " (error)", ips->ips_ref, hashval); if(ipsec_sadb_hash[hashval] == NULL) { return; } if (ips == ipsec_sadb_hash[hashval]) { ipsec_sadb_hash[hashval] = ipsec_sadb_hash[hashval]->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "successfully unhashed first ipsec_sa in chain.\n"); return; } else { struct ipsec_sa *ipstp; for (ipstp = ipsec_sadb_hash[hashval]; ipstp; ipstp = ipstp->ips_hnext) { if (ipstp->ips_hnext == ips) { ipstp->ips_hnext = ips->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "successfully unhashed link in ipsec_sa chain.\n"); return; } } } }
static int klips_set_ipc_saref(struct ipcm_cookie *ipc, xfrm_sec_unique_t ref) { struct ipsec_sa *sa1; struct sec_path *sp; sp = secpath_dup(NULL); if(!sp) return -EINVAL; sp->ref = ref; KLIPS_PRINT(debug_mast, "klips_debug:klips_set_ipc_saref: " "sending with saref=%u\n", sp->ref); sa1 = ipsec_sa_getbyref(sp->ref, IPSEC_REFOTHER); if(sa1 && sa1->ips_out) { ipc->oif = sa1->ips_out->ifindex; KLIPS_PRINT(debug_mast, "klips_debug:klips_set_ipc_saref: " "setting oif: %d\n", ipc->oif); } ipsec_sa_put(sa1, IPSEC_REFOTHER); ipc->sp = sp; return 0; }
struct ipsec_sa * __ipsec_sa_get(struct ipsec_sa *ips, const char *func, int line) { if (ips == NULL) return NULL; #ifdef CONFIG_KLIPS_DEBUG if(debug_xform) { char sa[SATOT_BUF]; size_t sa_len; sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); KLIPS_PRINT(debug_xform, "ipsec_sa_get: " "ipsec_sa %p SA:%s, ref:%d reference count (%d++) incremented by %s:%d.\n", ips, sa_len ? sa : " (error)", ips->ips_ref, atomic_read(&ips->ips_refcount), func, line); } #endif atomic_inc(&ips->ips_refcount); // check to make sure we were not deleted if (ips->ips_marked_deleted) { // we cannot use this reference ipsec_sa_put (ips); ips = NULL; } return ips; }
static void ipsec_mast_xsm_complete(struct ipsec_xmit_state *ixs, enum ipsec_xmit_value stat) { if (stat != IPSEC_XMIT_OK) { KLIPS_PRINT(debug_mast, "klips_debug:ipsec_mast_xsm_complete: " "ipsec_xsm failed: %d\n", stat); goto cleanup; } /* do any final NAT-encapsulation */ stat = ipsec_nat_encap(ixs); if (stat != IPSEC_XMIT_OK) goto cleanup; ipsec_xmit_send(ixs); cleanup: ipsec_xmit_cleanup(ixs); if (ixs->ipsp) { ipsec_sa_put(ixs->ipsp, IPSEC_REFOTHER); ixs->ipsp = NULL; } if (ixs->skb) { ipsec_kfree_skb(ixs->skb); ixs->skb = NULL; } ipsec_xmit_state_delete(ixs); }
int ipsec_sadb_free(void) { int error = 0; KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_free: " "freeing SArefTable memory.\n"); /* clean up SA reference table */ /* go through the ref table and clean out all the SAs if any are left and free table memory */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_free: " "removing SAref entries and tables.\n"); { unsigned table, entry; for (table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_free: " "removing SAref table=%u.\n", table); if (ipsec_sadb.refTable[table] == NULL) { KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_free: " "removed %u used refTables.\n", table); break; } for (entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { if (ipsec_sadb.refTable[table]->entry[entry] != NULL) { struct ipsec_sa *sa1 = ipsec_sadb.refTable[table]-> entry[entry ]; BUG_ON(atomic_read( &sa1->ips_refcount) == 1); ipsec_sa_put(sa1, IPSEC_REFSAADD); ipsec_sadb.refTable[table]->entry[entry ] = NULL; } } kfree(ipsec_sadb.refTable[table]); ipsec_sadb.refTable[table] = NULL; } } return error; }
int ipsec_sa_intern(struct ipsec_sa *ips) { int error = 0; IPsecSAref_t ref = ips->ips_ref; if(ref == IPSEC_SAREF_NULL) { ref = ipsec_SAref_alloc(&error); /* pass in error return by pointer */ KLIPS_PRINT(debug_xform, "ipsec_sa_intern: " "allocated ref=%u for sa %p\n", ref, ips); if(ref == IPSEC_SAREF_NULL) { KLIPS_PRINT(debug_xform, "ipsec_sa_intern: " "SAref allocation error\n"); return error; } ips->ips_ref = ref; } error = ipsec_saref_verify_slot(ref); if(error) { return error; } ipsec_sa_get(ips, IPSEC_REFINTERN); /* * if there is an existing SA at this reference, then free it * note, that nsa might == ips!. That's okay, we just incremented * the reference count above. */ { struct ipsec_sa *nsa = IPsecSAref2SA(ref); if(nsa) { ipsec_sa_put(nsa, IPSEC_REFINTERN); } } KLIPS_PRINT(debug_xform, "ipsec_sa_intern: " "SAref[%d]=%p\n", ips->ips_ref, ips); IPsecSAref2SA(ips->ips_ref) = ips; /* return OK */ return 0; }
static void klips_get_secpath_refs(struct sec_path *sp, xfrm_sec_unique_t *refme, xfrm_sec_unique_t *refhim) { struct ipsec_sa *sa1; if(sp==NULL) return; KLIPS_PRINT(debug_rcv, "klips_debug:klips_get_secpath_refs: " "retrieving saref=%u from sp=%p\n", sp->ref, sp); *refme = sp->ref; sa1 = ipsec_sa_getbyref(sp->ref, IPSEC_REFOTHER); *refhim = sa1 ? sa1->ips_refhim : 0; if(sa1) ipsec_sa_put(sa1, IPSEC_REFOTHER); }
void ipsec_sa_untern(struct ipsec_sa *ips) { IPsecSAref_t ref = ips->ips_ref; int error; /* verify that we are removing correct item! */ error = ipsec_saref_verify_slot(ref); if (error) return; if (IPsecSAref2SA(ref) == ips) { IPsecSAref2SA(ref) = NULL; ipsec_sa_put(ips, IPSEC_REFINTERN); } else { KLIPS_PRINT(debug_xform, "ipsec_sa_untern: " "ref=%u -> %p but untern'ing %p\n", ref, IPsecSAref2SA(ref), ips); } }
int main(char *argv[], int argc) { int error; struct sockaddr_in saddr,daddr; struct sockaddr_in saddr2,daddr2; void *main_talloc = NULL; struct ipsec_sa *sa, *sa1; char auth1[]={0x87, 0x65, 0x87, 0x65, 0x87, 0x65, 0x87, 0x65, 0x87, 0x65, 0x87, 0x65, 0x87, 0x65, 0x87, 0x65}; char enc[] ={0x40, 0x43, 0x43, 0x45, 0x45, 0x46, 0x46, 0x49, 0x49, 0x4a, 0x4a, 0x4c, 0x4c, 0x4f, 0x4f, 0x51, 0x51, 0x52, 0x52, 0x54, 0x54, 0x57, 0x57, 0x58}; debug_xform = 1; init_kmalloc(); debug_tunnel=0xffffffff; debug_xmit=0xffffffff; sysctl_ipsec_debug_verbose = 1; prng_init(&ipsec_prng, seed, sizeof(seed)); ipsec_sadb_init(); ipsec_alg_init(); { sa1 = ipsec_sa_alloc(&error); assert(error == 0); ipsec_sa_intern(sa1); sa1->ips_seq = 1; sa1->ips_pid = 10; sa1->ips_said.spi = htonl(0x12345678); sa1->ips_said.proto = IPPROTO_IPIP; sa1->ips_said.dst.u.v4.sin_addr.s_addr = htonl(0xc001022D); sa1->ips_state = SADB_SASTATE_MATURE; daddr2.sin_addr.s_addr = htonl(0xc001022D); saddr2.sin_addr.s_addr = htonl(0xc0010217); sa1->ips_addr_s = (struct sockaddr *)&saddr2; sa1->ips_addr_d = (struct sockaddr *)&daddr2; } { sa = ipsec_sa_alloc(&error); assert(error == 0); ipsec_sa_intern(sa); sa->ips_said.spi = htonl(0x12345678); sa->ips_said.proto = IPPROTO_ESP; sa->ips_said.dst.u.v4.sin_addr.s_addr = htonl(0xc001022D); sa->ips_said.dst.u.v4.sin_family = AF_INET; sa->ips_seq = 1; sa->ips_pid = 10; sa->ips_inext = sa1; { /* make a copy so that ipsec_sa_init() can zero it out */ char *auth = talloc_size(main_talloc, AHMD596_KLEN); memcpy(auth, auth1, AHMD596_KLEN); sa->ips_authalg = AH_MD5; sa->ips_key_bits_a = AHMD596_KLEN * 8; sa->ips_key_a = auth; } sa->ips_encalg = ESP_3DES; sa->ips_key_bits_e = 192; sa->ips_iv_bits = 128; sa->ips_key_e_size = 0; sa->ips_key_e = talloc_memdup(main_talloc, enc, sa->ips_key_bits_e); sa->ips_state = SADB_SASTATE_MATURE; daddr.sin_addr.s_addr = htonl(0xc001022D); saddr.sin_addr.s_addr = htonl(0xc0010217); sa->ips_addr_s = (struct sockaddr *)&saddr; sa->ips_addr_d = (struct sockaddr *)&daddr; ipsec_sa_add(sa); assert(ipsec_sa_init(sa) == 0); ipsec_sa_put(sa); } { struct ipsec_xmit_state ixs_mem; struct ipsec_xmit_state *ixs = &ixs_mem; enum ipsec_xmit_value stat; struct net_device_stats stats; int iphlen; struct sk_buff *skb = skbFromArray(packet2, packet2_len); skb->nh.raw = skb_pull(skb, skb->mac_len); iphlen = (skb->nh.iph->ihl<<2); /* do not use skb_pull, since data should stay at IP header */ skb->h.raw = skb->nh.raw + iphlen; memset((caddr_t)ixs, 0, sizeof(*ixs)); memset(&stats, 0, sizeof(stats)); ixs->stats = &stats; ixs->oskb = NULL; ixs->saved_header = NULL; /* saved copy of the hard header */ ixs->route = NULL; memset((caddr_t)&(ixs->ips), 0, sizeof(ixs->ips)); ixs->dev = NULL; ixs->skb = skb; ixs->physmtu = 1500; ixs->cur_mtu = 1500; ixs->outgoing_said.spi = htonl(0x12345678); ixs->outgoing_said.proto = IPPROTO_ESP; ixs->outgoing_said.dst.u.v4.sin_family = AF_INET; ixs->outgoing_said.dst.u.v4.sin_addr.s_addr = htonl(0xc001022D); stat = ipsec_xmit_sanity_check_skb(ixs); assert(stat == IPSEC_XMIT_OK); #if 0 stat = ipsec_tunnel_strip_hard_header(ixs); assert(stat == IPSEC_XMIT_OK); stat = ipsec_tunnel_SAlookup(ixs); assert(stat == IPSEC_XMIT_OK); #endif stat = ipsec_xmit_encap_bundle(ixs); assert(stat == IPSEC_XMIT_OK); #if 0 stat = ipsec_tunnel_restore_hard_header(ixs); #endif ipsec_print_ip(ixs->iph); } return 0; }
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; }
int ipsec_sa_wipe(struct ipsec_sa *ips) { int hashval; struct ipsec_sa **tpp; if (ips == NULL) return -ENODATA; #if IPSEC_SA_REF_CODE /* remove me from the SArefTable */ if (debug_xform) { char sa[SATOT_BUF]; size_t sa_len; struct IPsecSArefSubTable *subtable = NULL; if (IPsecSAref2table(IPsecSA2SAref(ips)) < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES && ipsec_sadb.refTable != NULL) subtable = ipsec_sadb.refTable[ IPsecSAref2table(IPsecSA2SAref(ips))]; sa_len = KLIPS_SATOT(debug_xform, &ips->ips_said, 0, sa, sizeof(sa)); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_wipe: " "removing SA=%s(0p%p), SAref=%d, table=%d(0p%p), entry=%d from the refTable.\n", sa_len ? sa : " (error)", ips, ips->ips_ref, IPsecSAref2table(IPsecSA2SAref(ips)), subtable, subtable ? IPsecSAref2entry(IPsecSA2SAref(ips)) : 0); } if (ips->ips_ref != IPSEC_SAREF_NULL) { struct IPsecSArefSubTable *subtable = NULL; int ref_table = IPsecSAref2table(IPsecSA2SAref(ips)); int ref_entry = IPsecSAref2entry(IPsecSA2SAref(ips)); if (ref_table < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES) { subtable = ipsec_sadb.refTable[ref_table]; if (subtable != NULL && subtable->entry[ref_entry] == ips) { subtable->entry[ref_entry] = NULL; } } ips->ips_ref = IPSEC_SAREF_NULL; } #endif /* IPSEC_SA_REF_CODE */ /* paranoid clean up */ if (ips->ips_addr_s != NULL) { memset((caddr_t)(ips->ips_addr_s), 0, ips->ips_addr_s_size); kfree(ips->ips_addr_s); } ips->ips_addr_s = NULL; if (ips->ips_addr_d != NULL) { memset((caddr_t)(ips->ips_addr_d), 0, ips->ips_addr_d_size); kfree(ips->ips_addr_d); } ips->ips_addr_d = NULL; if (ips->ips_addr_p != NULL) { memset((caddr_t)(ips->ips_addr_p), 0, ips->ips_addr_p_size); kfree(ips->ips_addr_p); } ips->ips_addr_p = NULL; if (ips->ips_natt_oa) { memset((caddr_t)(ips->ips_natt_oa), 0, ips->ips_natt_oa_size); kfree(ips->ips_natt_oa); } ips->ips_natt_oa = NULL; if (ips->ips_key_a != NULL) { #ifdef CONFIG_KLIPS_ALG if (ips->ips_alg_auth && ips->ips_alg_auth->ixt_a_destroy_key) { ips->ips_alg_auth->ixt_a_destroy_key(ips->ips_alg_auth, ips->ips_key_a); } else #endif { memset((caddr_t)(ips->ips_key_a), 0, ips->ips_key_a_size); kfree(ips->ips_key_a); } } ips->ips_key_a = NULL; if (ips->ips_key_e != NULL) { #ifdef CONFIG_KLIPS_ALG if (ips->ips_alg_enc && ips->ips_alg_enc->ixt_e_destroy_key) { ips->ips_alg_enc->ixt_e_destroy_key(ips->ips_alg_enc, ips->ips_key_e); } else #endif { memset((caddr_t)(ips->ips_key_e), 0, ips->ips_key_e_size); kfree(ips->ips_key_e); } } ips->ips_key_e = NULL; if (ips->ips_iv != NULL) { memset((caddr_t)(ips->ips_iv), 0, ips->ips_iv_size); kfree(ips->ips_iv); } ips->ips_iv = NULL; #ifdef CONFIG_KLIPS_OCF if (ips->ocf_in_use) ipsec_ocf_sa_free(ips); #endif if (ips->ips_ident_s.data != NULL) { memset((caddr_t)(ips->ips_ident_s.data), 0, ips->ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident)); kfree(ips->ips_ident_s.data); } ips->ips_ident_s.data = NULL; if (ips->ips_ident_d.data != NULL) { memset((caddr_t)(ips->ips_ident_d.data), 0, ips->ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident)); kfree(ips->ips_ident_d.data); } ips->ips_ident_d.data = NULL; #ifdef CONFIG_KLIPS_ALG if (ips->ips_alg_enc || ips->ips_alg_auth) ipsec_alg_sa_wipe(ips); ips->ips_alg_enc = NULL; ips->ips_alg_auth = NULL; #endif if (ips->ips_prev) ips->ips_prev->ips_next = ips->ips_next; if (ips->ips_next) { ips->ips_next->ips_prev = ips->ips_prev; ipsec_sa_put(ips->ips_next, IPSEC_REFALLOC); } ips->ips_next = NULL; ips->ips_prev = NULL; hashval = IPS_HASH(&ips->ips_said); tpp = &ipsec_sadb_hash[hashval]; while (*tpp) { if (*tpp == ips) *tpp = ips->ips_hnext; else tpp = &((*tpp)->ips_hnext); } if (ips->ips_hnext) ipsec_sa_put(ips->ips_hnext, IPSEC_REFALLOC); ips->ips_hnext = NULL; BUG_ON(atomic_read(&ips->ips_refcount) != 0); #ifdef IPSEC_SA_RECOUNT_DEBUG if (ips == ipsec_sa_raw) { ipsec_sa_raw = ips->ips_raw; } else { struct ipsec_sa *raw = ipsec_sa_raw; while (raw) { if (raw->ips_raw == ips) { raw->ips_raw = ips->ips_raw; break; } raw = raw->ips_raw; } } #endif if (ips->ips_out != NULL) { ipsec_dev_put(ips->ips_out); ips->ips_out = NULL; } memset((caddr_t)ips, 0, sizeof(*ips)); kfree(ips); ips = NULL; return 0; }
int ipsec_sadb_cleanup(__u8 proto) { unsigned i; int error = 0; struct ipsec_sa *ips; /* struct ipsec_sa *ipsnext, **ipsprev; */ /* char sa[SATOT_BUF]; */ /* size_t sa_len; */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_cleanup: " "cleaning up proto=%d.\n", proto); spin_lock_bh(&tdb_lock); for (i = 0; i < SADB_HASHMOD; i++) { ips = ipsec_sadb_hash[i]; while (ips) { ipsec_sadb_hash[i] = ips->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips, IPSEC_REFSAADD); ips = ipsec_sadb_hash[i]; } } /* errlab: */ spin_unlock_bh(&tdb_lock); #if IPSEC_SA_REF_CODE /* clean up SA reference table */ /* go through the ref table and clean out all the SAs */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_cleanup: " "removing SAref entries and tables."); { unsigned table, entry; for (table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_cleanup: " "cleaning SAref table=%u.\n", table); if (ipsec_sadb.refTable[table] == NULL) { printk("\n"); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sadb_cleanup: " "cleaned %u used refTables.\n", table); break; } for (entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { if (ipsec_sadb.refTable[table]->entry[entry] != NULL) { struct ipsec_sa *sa1 = ipsec_sadb.refTable[table]-> entry[entry]; ipsec_sa_put(sa1, IPSEC_REFOTHER); ipsec_sadb.refTable[table]->entry[entry ] = NULL; } } } } #endif /* IPSEC_SA_REF_CODE */ return error; }
/* * The ipsec_sa table better be locked before it is handed in, * or races might happen. * * this routine assumes the SA has a refcount==0, and we free it. * we also assume that the pointers are already cleaned up. */ static int ipsec_sa_del(struct ipsec_sa *ips) { unsigned int hashval; struct ipsec_sa *ipstp; char sa[SATOT_BUF]; size_t sa_len; if (ips == NULL) { KLIPS_ERROR(debug_xform, "klips_error:ipsec_sa_del: " "null pointer passed in!\n"); return -ENODATA; } if (ips->ips_next) { struct ipsec_sa *in = ips->ips_next; ips->ips_next = NULL; ipsec_sa_put(in); } sa_len = KLIPS_SATOT(debug_xform, &ips->ips_said, 0, sa, sizeof(sa)); hashval = IPS_HASH(&ips->ips_said); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "deleting SA:%s (ref=%u), hashval=%d.\n", sa_len ? sa : " (error)", ips->ips_ref, hashval); if (ipsec_sadb_hash[hashval] == NULL) { /* if this is NULL, then we can be sure that the SA was never * added to the SADB, so we just free it. */ KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "no entries in ipsec_sa table for hash=%d (ref=%u) of SA:%s.\n", hashval, ips->ips_ref, sa_len ? sa : " (error)"); return -ENOENT; } if (ips == ipsec_sadb_hash[hashval]) { ipsec_sadb_hash[hashval] = ipsec_sadb_hash[hashval]->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "successfully deleted first ipsec_sa in chain.\n"); return 0; } else { for (ipstp = ipsec_sadb_hash[hashval]; ipstp; ipstp = ipstp->ips_hnext) { if (ipstp->ips_hnext == ips) { ipstp->ips_hnext = ips->ips_hnext; ips->ips_hnext = NULL; ipsec_sa_put(ips); KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "successfully deleted link in ipsec_sa chain.\n"); return 0; } } } KLIPS_PRINT(debug_xform, "klips_debug:ipsec_sa_del: " "no entries in linked list for hash=%d of SA:%s.\n", hashval, sa_len ? sa : " (error)"); return -ENOENT; }
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; }
IPSEC_PROCFS_DEBUG_NO_STATIC int ipsec_spi_format(struct ipsec_sa *sa_p, struct seq_file *seq) { char sa[SATOT_BUF]; char buf_s[SUBNETTOA_BUF]; char buf_d[SUBNETTOA_BUF]; size_t sa_len; ipsec_sa_get(sa_p, IPSEC_REFPROC); sa_len = satot(&sa_p->ips_said, 'x', sa, sizeof(sa)); seq_printf(seq, "%s ", sa_len ? sa : " (error)"); seq_printf(seq, "%s%s%s", IPS_XFORM_NAME(sa_p)); seq_printf(seq, ": dir=%s", (sa_p->ips_flags & EMT_INBOUND) ? "in " : "out"); if (sa_p->ips_addr_s) { sin_addrtot(sa_p->ips_addr_s, 0, buf_s, sizeof(buf_s)); seq_printf(seq, " src=%s", buf_s); } if ((sa_p->ips_said.proto == IPPROTO_IPIP) && (sa_p->ips_flags & (SADB_X_SAFLAGS_INFLOW |SADB_X_SAFLAGS_POLICYONLY))) { if (sa_p->ips_flow_s.u.v4.sin_family == AF_INET) { subnettoa(sa_p->ips_flow_s.u.v4.sin_addr, sa_p->ips_mask_s.u.v4.sin_addr, 0, buf_s, sizeof(buf_s)); subnettoa(sa_p->ips_flow_d.u.v4.sin_addr, sa_p->ips_mask_d.u.v4.sin_addr, 0, buf_d, sizeof(buf_d)); } else { subnet6toa(&sa_p->ips_flow_s.u.v6.sin6_addr, &sa_p->ips_mask_s.u.v6.sin6_addr, 0, buf_s, sizeof(buf_s)); subnet6toa(&sa_p->ips_flow_d.u.v6.sin6_addr, &sa_p->ips_mask_d.u.v6.sin6_addr, 0, buf_d, sizeof(buf_d)); } seq_printf(seq, " policy=%s->%s", buf_s, buf_d); } if (sa_p->ips_iv_bits) { int j; seq_printf(seq, " iv_bits=%dbits iv=0x", sa_p->ips_iv_bits); for (j = 0; j < sa_p->ips_iv_bits / 8; j++) { #ifdef CONFIG_KLIPS_OCF if (sa_p->ips_iv == NULL) { /* * ocf doesn't set the IV * so fake it for the test cases */ seq_printf(seq, "%02x", 0xA5 + j); } else #endif seq_printf(seq, "%02x", ((__u8*)sa_p->ips_iv)[j]); } } if (sa_p->ips_encalg || sa_p->ips_authalg) { if (sa_p->ips_replaywin) seq_printf(seq, " ooowin=%d", sa_p->ips_replaywin); if (sa_p->ips_errs.ips_replaywin_errs) seq_printf(seq, " ooo_errs=%d", sa_p->ips_errs.ips_replaywin_errs); if (sa_p->ips_replaywin_lastseq) seq_printf(seq, " seq=%d", sa_p->ips_replaywin_lastseq); if (sa_p->ips_replaywin_bitmap) seq_printf(seq, " bit=0x%Lx", sa_p->ips_replaywin_bitmap); if (sa_p->ips_replaywin_maxdiff) seq_printf(seq, " max_seq_diff=%d", sa_p->ips_replaywin_maxdiff); } if (sa_p->ips_flags & ~EMT_INBOUND) { seq_printf(seq, " flags=0x%x", sa_p->ips_flags & ~EMT_INBOUND); seq_printf(seq, "<"); /* flag printing goes here */ seq_printf(seq, ">"); } if (sa_p->ips_auth_bits) seq_printf(seq, " alen=%d", sa_p->ips_auth_bits); if (sa_p->ips_key_bits_a) seq_printf(seq, " aklen=%d", sa_p->ips_key_bits_a); if (sa_p->ips_errs.ips_auth_errs) seq_printf(seq, " auth_errs=%d", sa_p->ips_errs.ips_auth_errs); if (sa_p->ips_key_bits_e) seq_printf(seq, " eklen=%d", sa_p->ips_key_bits_e); if (sa_p->ips_errs.ips_encsize_errs) seq_printf(seq, " encr_size_errs=%d", sa_p->ips_errs.ips_encsize_errs); if (sa_p->ips_errs.ips_encpad_errs) seq_printf(seq, " encr_pad_errs=%d", sa_p->ips_errs.ips_encpad_errs); seq_printf(seq, " jiffies=%lu", jiffies); seq_printf(seq, " life(c,s,h)="); ipsec_lifetime_format(seq, "alloc", ipsec_life_countbased, &sa_p->ips_life.ipl_allocations); ipsec_lifetime_format(seq, "bytes", ipsec_life_countbased, &sa_p->ips_life.ipl_bytes); ipsec_lifetime_format(seq, "addtime", ipsec_life_timebased, &sa_p->ips_life.ipl_addtime); ipsec_lifetime_format(seq, "usetime", ipsec_life_timebased, &sa_p->ips_life.ipl_usetime); ipsec_lifetime_format(seq, "packets", ipsec_life_countbased, &sa_p->ips_life.ipl_packets); if (sa_p->ips_life.ipl_usetime.ipl_last) { /* XXX-MCR should be last? */ seq_printf(seq, " idle=%Ld", ipsec_jiffieshz_elapsed(jiffies/HZ, sa_p->ips_life.ipl_usetime.ipl_last)); } #ifdef CONFIG_KLIPS_IPCOMP if (sa_p->ips_said.proto == IPPROTO_COMP && (sa_p->ips_comp_ratio_dbytes || sa_p->ips_comp_ratio_cbytes)) { seq_printf(seq, " ratio=%Ld:%Ld", sa_p->ips_comp_ratio_dbytes, sa_p->ips_comp_ratio_cbytes); } #endif /* CONFIG_KLIPS_IPCOMP */ seq_printf(seq, " natencap="); switch (sa_p->ips_natt_type) { case 0: seq_printf(seq, "none"); break; case ESPINUDP_WITH_NON_IKE: seq_printf(seq, "nonike"); break; case ESPINUDP_WITH_NON_ESP: seq_printf(seq, "nonesp"); break; default: seq_printf(seq, "unknown"); break; } seq_printf(seq, " natsport=%d", sa_p->ips_natt_sport); seq_printf(seq, " natdport=%d", sa_p->ips_natt_dport); /* we decrement by one, because this SA has been referenced in order to dump this info */ seq_printf(seq, " refcount=%d", atomic_read(&sa_p->ips_refcount)-1); #ifdef IPSEC_SA_RECOUNT_DEBUG { int f; seq_printf(seq, "["); for (f = 0; f < sizeof(sa_p->ips_track); f++) seq_printf(seq, "%s%d", f == 0 ? "" : ",", sa_p->ips_track[f]); seq_printf(seq, "]"); } #endif seq_printf(seq, " ref=%d", sa_p->ips_ref); seq_printf(seq, " refhim=%d", sa_p->ips_refhim); if (sa_p->ips_out) { seq_printf(seq, " outif=%s:%d", sa_p->ips_out->name, sa_p->ips_transport_direct); } if (debug_xform) { seq_printf(seq, " reftable=%lu refentry=%lu", (unsigned long)IPsecSAref2table(sa_p->ips_ref), (unsigned long)IPsecSAref2entry(sa_p->ips_ref)); } seq_printf(seq, "\n"); ipsec_sa_put(sa_p, IPSEC_REFPROC); return 0; }