From 12bdf036e254041a2d72a73193c7bfa88614c85e Mon Sep 17 00:00:00 2001 From: itojun Date: Sun, 19 May 2002 08:22:12 +0000 Subject: [PATCH] pull in SPD lifetime management code. fix refcnt for SPD entries. sync w/kame XXX dead SPD entry lifetime - undergoing sakane's review --- sys/netkey/key.c | 307 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 276 insertions(+), 31 deletions(-) diff --git a/sys/netkey/key.c b/sys/netkey/key.c index 4b88a4417765..b4b15cbb4120 100644 --- a/sys/netkey/key.c +++ b/sys/netkey/key.c @@ -1,5 +1,5 @@ -/* $NetBSD: key.c,v 1.62 2002/05/19 08:12:55 itojun Exp $ */ -/* $KAME: key.c,v 1.203 2001/07/28 03:12:18 itojun Exp $ */ +/* $NetBSD: key.c,v 1.63 2002/05/19 08:22:12 itojun Exp $ */ +/* $KAME: key.c,v 1.234 2002/05/13 03:21:17 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -35,7 +35,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.62 2002/05/19 08:12:55 itojun Exp $"); +__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.63 2002/05/19 08:22:12 itojun Exp $"); #include "opt_inet.h" #include "opt_ipsec.h" @@ -332,6 +332,7 @@ static int key_spddump __P((struct socket *, struct mbuf *, static struct mbuf *key_setdumpsp __P((struct secpolicy *, u_int8_t, u_int32_t, u_int32_t)); static u_int key_getspreqmsglen __P((struct secpolicy *)); +static int key_spdexpire __P((struct secpolicy *)); static struct secashead *key_newsah __P((struct secasindex *)); static void key_delsah __P((struct secashead *)); static struct secasvar *key_newsav __P((struct mbuf *, @@ -355,6 +356,8 @@ static struct mbuf *key_setsadbident __P((u_int16_t, u_int16_t, caddr_t, int, u_int64_t)); #endif static struct mbuf *key_setsadbxsa2 __P((u_int8_t, u_int32_t, u_int32_t)); +static struct mbuf *key_setsadblifetime __P((u_int16_t, u_int32_t, + u_int64_t, u_int64_t, u_int64_t)); static struct mbuf *key_setsadbxpolicy __P((u_int16_t, u_int8_t, u_int32_t)); static void *key_newbuf __P((const void *, u_int)); @@ -428,6 +431,8 @@ static const char *key_getfqdn __P((void)); static const char *key_getuserfqdn __P((void)); #endif static void key_sa_chgstate __P((struct secasvar *, u_int8_t)); +static void key_sp_dead __P((struct secpolicy *)); +static void key_sp_unlink __P((struct secpolicy *)); static struct mbuf *key_alloc_mbuf __P((int)); struct callout key_timehandler_ch; @@ -484,6 +489,7 @@ found: KEY_CHKSPDIR(sp->spidx.dir, dir, "key_allocsp"); /* found a SPD entry */ + sp->lastused = time.tv_sec; sp->refcnt++; splx(s); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, @@ -494,8 +500,8 @@ found: } /* - * allocating a SA entry for a *OUTBOUND* packet. - * checking each request entries in SP, and acquire SA if need. + * allocating an SA entry for a *OUTBOUND* packet. + * checking each request entries in SP, and acquire an SA if need. * OUT: 0: there are valid requests. * ENOENT: policy may be valid, but SA with REQUIRE is on acquiring. */ @@ -955,17 +961,12 @@ key_delsp(sp) /* sanity check */ if (sp == NULL) - panic("key_delsp: NULL pointer is passed.\n"); - - sp->state = IPSEC_SPSTATE_DEAD; + panic("key_delsp: NULL pointer is passed."); if (sp->refcnt > 0) - return; /* can't free */ + panic("key_delsp: called with positive refcnt"); s = splsoftnet(); /*called from softclock()*/ - /* remove from SP index */ - if (__LIST_CHAINED(sp)) - LIST_REMOVE(sp, chain); { struct ipsecrequest *isr = sp->req, *nextisr; @@ -1460,11 +1461,11 @@ fail: /* * SADB_X_SPDADD, SADB_X_SPDSETIDX or SADB_X_SPDUPDATE processing * add a entry to SP database, when received - * + * * from the user(?). * Adding to SP database, * and send - * + * * to the socket which was send. * * SPDADD set a unique policy entry. @@ -1481,6 +1482,7 @@ key_spdadd(so, m, mhp) { struct sadb_address *src0, *dst0; struct sadb_x_policy *xpl0, *xpl; + struct sadb_lifetime *lft = NULL; struct secpolicyindex spidx; struct secpolicy *newsp; int error; @@ -1501,6 +1503,14 @@ key_spdadd(so, m, mhp) ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n")); return key_senderror(so, m, EINVAL); } + if (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL) { + if (mhp->extlen[SADB_EXT_LIFETIME_HARD] + < sizeof(struct sadb_lifetime)) { + ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n")); + return key_senderror(so, m, EINVAL); + } + lft = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD]; + } src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; @@ -1552,8 +1562,10 @@ key_spdadd(so, m, mhp) newsp = key_getsp(&spidx); if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { if (newsp) { - newsp->state = IPSEC_SPSTATE_DEAD; - key_freesp(newsp); + key_sp_dead(newsp); + key_freesp(newsp); /* ref gained by key_getsp */ + key_sp_unlink(newsp); + newsp = NULL; } } else { if (newsp != NULL) { @@ -1612,6 +1624,11 @@ key_spdadd(so, m, mhp) } #endif + newsp->created = time.tv_sec; + newsp->lastused = time.tv_sec; + newsp->lifetime = lft ? lft->sadb_lifetime_addtime : 0; + newsp->validtime = lft ? lft->sadb_lifetime_usetime : 0; + newsp->refcnt = 1; /* do not reclaim until I say I do */ newsp->state = IPSEC_SPSTATE_ALIVE; LIST_INSERT_TAIL(&sptree[newsp->spidx.dir], newsp, secpolicy, chain); @@ -1635,8 +1652,15 @@ key_spdadd(so, m, mhp) int off; /* create new sadb_msg to reply. */ - n = key_gather_mbuf(m, mhp, 2, 4, SADB_EXT_RESERVED, - SADB_X_EXT_POLICY, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + if (lft) { + n = key_gather_mbuf(m, mhp, 2, 5, SADB_EXT_RESERVED, + SADB_X_EXT_POLICY, SADB_EXT_LIFETIME_HARD, + SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + } else { + n = key_gather_mbuf(m, mhp, 2, 4, SADB_EXT_RESERVED, + SADB_X_EXT_POLICY, + SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + } if (!n) return key_senderror(so, m, ENOBUFS); @@ -1772,8 +1796,10 @@ key_spddelete(so, m, mhp) /* save policy id to buffer to be returned. */ xpl0->sadb_x_policy_id = sp->id; - sp->state = IPSEC_SPSTATE_DEAD; - key_freesp(sp); + key_sp_dead(sp); + key_freesp(sp); /* ref gained by key_getsp */ + key_sp_unlink(sp); + sp = NULL; /* invalidate all cached SPD pointers on pcb */ ipsec_invalpcbcacheall(); @@ -1837,8 +1863,10 @@ key_spddelete2(so, m, mhp) key_senderror(so, m, EINVAL); } - sp->state = IPSEC_SPSTATE_DEAD; - key_freesp(sp); + key_sp_dead(sp); + key_freesp(sp); /* ref gained by key_getsp */ + key_sp_unlink(sp); + sp = NULL; /* invalidate all cached SPD pointers on pcb */ ipsec_invalpcbcacheall(); @@ -2036,7 +2064,7 @@ key_spdflush(so, m, mhp) const struct sadb_msghdr *mhp; { struct sadb_msg *newmsg; - struct secpolicy *sp; + struct secpolicy *sp, *nextsp; u_int dir; /* sanity check */ @@ -2047,8 +2075,13 @@ key_spdflush(so, m, mhp) return key_senderror(so, m, EINVAL); for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - LIST_FOREACH(sp, &sptree[dir], chain) { - sp->state = IPSEC_SPSTATE_DEAD; + for (sp = LIST_FIRST(&sptree[dir]); sp != NULL; sp = nextsp) { + nextsp = LIST_NEXT(sp, chain); + if (sp->state == IPSEC_SPSTATE_DEAD) + continue; + key_sp_dead(sp); + key_sp_unlink(sp); + sp = NULL; } } @@ -2155,6 +2188,18 @@ key_setdumpsp(sp, type, seq, pid) goto fail; m_cat(result, m); + m = key_setsadblifetime(SADB_EXT_LIFETIME_CURRENT, + 0, 0, (u_int64_t)sp->created, (u_int64_t)sp->lastused); + if (!m) + goto fail; + m_cat(result, m); + + m = key_setsadblifetime(SADB_EXT_LIFETIME_HARD, + 0, 0, (u_int64_t)sp->lifetime, (u_int64_t)sp->validtime); + if (!m) + goto fail; + m_cat(result, m); + if ((result->m_flags & M_PKTHDR) == 0) goto fail; @@ -2210,6 +2255,127 @@ key_getspreqmsglen(sp) return tlen; } +/* + * SADB_SPDEXPIRE processing + * send + * + * to KMD by PF_KEY. + * + * OUT: 0 : succeed + * others : error number + */ +static int +key_spdexpire(sp) + struct secpolicy *sp; +{ + int s; + struct mbuf *result = NULL, *m; + int len; + int error = -1; + struct sadb_lifetime *lt; + + /* XXX: Why do we lock ? */ +#ifdef __NetBSD__ + s = splsoftnet(); /*called from softclock()*/ +#else + s = splnet(); /*called from softclock()*/ +#endif + + /* sanity check */ + if (sp == NULL) + panic("key_spdexpire: NULL pointer is passed.\n"); + + /* set msg header */ + m = key_setsadbmsg(SADB_X_SPDEXPIRE, 0, 0, 0, 0, 0); + if (!m) { + error = ENOBUFS; + goto fail; + } + result = m; + + /* create lifetime extension (current and hard) */ + len = PFKEY_ALIGN8(sizeof(*lt)) * 2; + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + error = ENOBUFS; + goto fail; + } + bzero(mtod(m, caddr_t), len); + lt = mtod(m, struct sadb_lifetime *); + lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); + lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; + lt->sadb_lifetime_allocations = 0; + lt->sadb_lifetime_bytes = 0; + lt->sadb_lifetime_addtime = sp->created; + lt->sadb_lifetime_usetime = sp->lastused; + lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2); + lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); + lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; + lt->sadb_lifetime_allocations = 0; + lt->sadb_lifetime_bytes = 0; + lt->sadb_lifetime_addtime = sp->lifetime; + lt->sadb_lifetime_usetime = sp->validtime; + m_cat(result, m); + + /* set sadb_address for source */ + m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&sp->spidx.src, + sp->spidx.prefs, sp->spidx.ul_proto); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); + + /* set sadb_address for destination */ + m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&sp->spidx.dst, + sp->spidx.prefd, sp->spidx.ul_proto); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); + + /* set secpolicy */ + m = key_sp2msg(sp); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); + + if ((result->m_flags & M_PKTHDR) == 0) { + error = EINVAL; + goto fail; + } + + if (result->m_len < sizeof(struct sadb_msg)) { + result = m_pullup(result, sizeof(struct sadb_msg)); + if (result == NULL) { + error = ENOBUFS; + goto fail; + } + } + + result->m_pkthdr.len = 0; + for (m = result; m; m = m->m_next) + result->m_pkthdr.len += m->m_len; + + mtod(result, struct sadb_msg *)->sadb_msg_len = + PFKEY_UNIT64(result->m_pkthdr.len); + + return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED); + + fail: + if (result) + m_freem(result); + splx(s); + return error; +} + /* %%% SAD management */ /* * allocating a memory for new SA head, and copy from the values of mhp. @@ -2409,7 +2575,7 @@ key_delsav(sav) panic("key_delsav: NULL pointer is passed.\n"); if (sav->refcnt > 0) - return; /* can't free */ + panic("key_delsav: called with positive refcnt"); /* remove from SA header */ if (__LIST_CHAINED(sav)) @@ -2757,7 +2923,12 @@ key_setsaval(sav, m, mhp) error = ENOBUFS; goto fail; } - /* to be initialize ? */ + /* we no longer support byte lifetime */ + if (sav->lft_h->sadb_lifetime_bytes) { + error = EINVAL; + goto fail; + } + /* initialize? */ } lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_SOFT]; @@ -2773,7 +2944,12 @@ key_setsaval(sav, m, mhp) error = ENOBUFS; goto fail; } - /* to be initialize ? */ + /* we no longer support byte lifetime */ + if (sav->lft_s->sadb_lifetime_bytes) { + error = EINVAL; + goto fail; + } + /* initialize? */ } } @@ -3348,6 +3524,40 @@ key_setsadbxsa2(mode, seq, reqid) return m; } +/* + * set data into sadb_lifetime + */ +static struct mbuf * +key_setsadblifetime(type, alloc, bytes, addtime, usetime) + u_int16_t type; + u_int32_t alloc; + u_int64_t bytes, addtime, usetime; +{ + struct mbuf *m; + struct sadb_lifetime *p; + size_t len; + + len = PFKEY_ALIGN8(sizeof(struct sadb_lifetime)); + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return NULL; + } + + p = mtod(m, struct sadb_lifetime *); + + bzero(p, len); + p->sadb_lifetime_len = PFKEY_UNIT64(len); + p->sadb_lifetime_exttype = type; + p->sadb_lifetime_allocations = alloc; + p->sadb_lifetime_bytes = bytes; + p->sadb_lifetime_addtime = addtime; + p->sadb_lifetime_usetime = usetime; + + return m; +} + /* * set data into sadb_x_policy */ @@ -3854,11 +4064,26 @@ key_timehandler(arg) for (sp = LIST_FIRST(&sptree[dir]); sp != NULL; sp = nextsp) { - nextsp = LIST_NEXT(sp, chain); - if (sp->state == IPSEC_SPSTATE_DEAD) - key_freesp(sp); + if (sp->state == IPSEC_SPSTATE_DEAD) { + key_sp_unlink(sp); /*XXX*/ + sp = NULL; + continue; + } + + if (sp->lifetime == 0 && sp->validtime == 0) + continue; + + /* the deletion will occur next time */ + if ((sp->lifetime && + tv.tv_sec - sp->created > sp->lifetime) || + (sp->validtime && + tv.tv_sec - sp->lastused > sp->validtime)) { + key_sp_dead(sp); + key_spdexpire(sp); + continue; + } } } } @@ -7087,6 +7312,26 @@ key_sa_stir_iv(sav) key_randomfill(sav->iv, sav->ivlen); } +static void +key_sp_dead(sp) + struct secpolicy *sp; +{ + + /* mark the SP dead */ + sp->state = IPSEC_SPSTATE_DEAD; +} + +static void +key_sp_unlink(sp) + struct secpolicy *sp; +{ + + /* remove from SP index */ + if (__LIST_CHAINED(sp)) + LIST_REMOVE(sp, chain); + key_freesp(sp); +} + /* XXX too much? */ static struct mbuf * key_alloc_mbuf(l)