Exemplo n.º 1
0
/*
 * 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;
			}
		}
	}
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
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;
}
Exemplo n.º 4
0
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);
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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;
}
Exemplo n.º 7
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);
}
Exemplo n.º 8
0
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);
	}

}
Exemplo n.º 9
0
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;
}
Exemplo n.º 12
0
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;
}
Exemplo n.º 13
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;
}
Exemplo n.º 14
0
/*
 * 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;
}
Exemplo n.º 15
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;
}
Exemplo n.º 16
0
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;
}