From 373abd94036be0e5643f5134aa9fb8bb694d1624 Mon Sep 17 00:00:00 2001 From: ume Date: Tue, 4 Nov 2003 16:02:05 +0000 Subject: - cleanup SP refcnt issue. - share policy-on-socket for listening socket. - don't copy policy-on-socket at all. secpolicy no longer contain spidx, which saves a lot of memory. - deep-copy pcb policy if it is an ipsec policy. assign ID field to all SPD entries. make it possible for racoon to grab SPD entry on pcb. - fixed the order of searching SA table for packets. - fixed to get a security association header. a mode is always needed to compare them. - fixed that the incorrect time was set to sadb_comb_{hard|soft}_usetime. - disallow port spec for tunnel mode policy (as we don't reassemble). - an user can define a policy-id. - clear enc/auth key before freeing. - fixed that the kernel crashed when key_spdacquire() was called because key_spdacquire() had been implemented imcopletely. - preparation for 64bit sequence number. - maintain ordered list of SA, based on SA id. - cleanup secasvar management; refcnt is key.c responsibility; alloc/free is keydb.c responsibility. - cleanup, avoid double-loop. - use hash for spi-based lookup. - mark persistent SP "persistent". XXX in theory refcnt should do the right thing, however, we have "spdflush" which would touch all SPs. another solution would be to de-register persistent SPs from sptree. - u_short -> u_int16_t - reduce kernel stack usage by auto variable secasindex. - clarify function name confusion. ipsec_*_policy -> ipsec_*_pcbpolicy. - avoid variable name confusion. (struct inpcbpolicy *)pcb_sp, spp (struct secpolicy **), sp (struct secpolicy *) - count number of ipsec encapsulations on ipsec4_output, so that we can tell ip_output() how to handle the packet further. - When the value of the ul_proto is ICMP or ICMPV6, the port field in "src" of the spidx specifies ICMP type, and the port field in "dst" of the spidx specifies ICMP code. - avoid from applying IPsec transport mode to the packets when the kernel forwards the packets. Tested by: nork Obtained from: KAME --- sys/netinet/in_pcb.c | 18 +- sys/netinet/ip_input.c | 2 +- sys/netinet/ip_output.c | 13 +- sys/netinet/tcp_output.c | 7 + sys/netinet/tcp_syncache.c | 6 +- sys/netinet6/icmp6.c | 11 +- sys/netinet6/ip6_forward.c | 54 +- sys/netinet6/ip6_output.c | 17 +- sys/netinet6/ipsec.c | 1177 ++++++++++++++++++++++++++------------------ sys/netinet6/ipsec.h | 70 ++- sys/netinet6/ipsec6.h | 11 +- sys/netinet6/nd6.c | 4 + sys/netinet6/nd6_nbr.c | 8 + sys/netinet6/raw_ip6.c | 11 +- sys/netinet6/udp6_output.c | 6 + sys/netinet6/udp6_usrreq.c | 6 +- sys/netkey/key.c | 1167 ++++++++++++++++++++++++------------------- sys/netkey/key.h | 21 +- sys/netkey/key_debug.c | 25 +- sys/netkey/keydb.c | 79 ++- sys/netkey/keydb.h | 23 +- 21 files changed, 1655 insertions(+), 1081 deletions(-) diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index ec51ad4..e473f2f 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -79,7 +79,6 @@ #include #include -#define IPSEC #endif /* FAST_IPSEC */ struct in_addr zeroin_addr; @@ -162,7 +161,7 @@ in_pcballoc(so, pcbinfo, td) struct thread *td; { register struct inpcb *inp; -#ifdef IPSEC +#if defined(IPSEC) || defined(FAST_IPSEC) int error; #endif inp = uma_zalloc(pcbinfo->ipi_zone, M_NOWAIT | M_ZERO); @@ -171,8 +170,12 @@ in_pcballoc(so, pcbinfo, td) inp->inp_gencnt = ++pcbinfo->ipi_gencnt; inp->inp_pcbinfo = pcbinfo; inp->inp_socket = so; -#ifdef IPSEC +#if defined(IPSEC) || defined(FAST_IPSEC) +#ifdef FAST_IPSEC error = ipsec_init_policy(so, &inp->inp_sp); +#else + error = ipsec_init_pcbpolicy(so, &inp->inp_sp); +#endif if (error != 0) { uma_zfree(pcbinfo->ipi_zone, inp); return error; @@ -473,6 +476,10 @@ in_pcbconnect(inp, nam, td) inp->inp_faddr.s_addr = faddr; inp->inp_fport = fport; in_pcbrehash(inp); +#ifdef IPSEC + if (inp->inp_socket->so_type == SOCK_STREAM) + ipsec_pcbconn(inp->inp_sp); +#endif if (anonport) inp->inp_flags |= INP_ANONPORT; return (0); @@ -655,6 +662,9 @@ in_pcbdisconnect(inp) in_pcbrehash(inp); if (inp->inp_socket->so_state & SS_NOFDREF) in_pcbdetach(inp); +#ifdef IPSEC + ipsec_pcbdisconn(inp->inp_sp); +#endif } void @@ -664,7 +674,7 @@ in_pcbdetach(inp) struct socket *so = inp->inp_socket; struct inpcbinfo *ipi = inp->inp_pcbinfo; -#ifdef IPSEC +#if defined(IPSEC) || defined(FAST_IPSEC) ipsec4_delete_pcbpolicy(inp); #endif /*IPSEC*/ inp->inp_gencnt = ++ipi->ipi_gencnt; diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 2518154..09f3f41 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -492,7 +492,7 @@ tooshort: /* * Bypass packet filtering for packets from a tunnel (gif). */ - if (ipsec_gethist(m, NULL)) + if (ipsec_getnhist(m)) goto pass; #endif #if defined(FAST_IPSEC) && !defined(IPSEC_FILTERGIF) diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index aedab7f..0831784 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -144,6 +144,7 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, struct in_addr pkt_dst; #ifdef IPSEC struct route iproute; + struct socket *so; struct secpolicy *sp = NULL; #endif #ifdef FAST_IPSEC @@ -195,6 +196,11 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, } m = m0; +#ifdef IPSEC + so = ipsec_getsocket(m); + (void)ipsec_setsocket(m, NULL); +#endif /*IPSEC*/ + M_ASSERTPKTHDR(m); #ifndef FAST_IPSEC KASSERT(ro != NULL, ("ip_output: no route, proto %d", @@ -488,10 +494,11 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, sendit: #ifdef IPSEC /* get SP for this packet */ - if (inp == NULL) - sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, flags, &error); + if (so == NULL) + sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, + flags, &error); else - sp = ipsec4_getpolicybypcb(m, IPSEC_DIR_OUTBOUND, inp, &error); + sp = ipsec4_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); if (sp == NULL) { ipsecstat.out_inval++; diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index 1267aaf..b7ddc23 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -880,6 +880,13 @@ send: : NULL); /* TODO: IPv6 IP6TOS_ECT bit on */ +#ifdef IPSEC + if (ipsec_setsocket(m, so) != 0) { + m_freem(m); + error = ENOBUFS; + goto out; + } +#endif /*IPSEC*/ error = ip6_output(m, tp->t_inpcb->in6p_outputopts, &tp->t_inpcb->in6p_route, diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c index 902e711..c83e9b2 100644 --- a/sys/netinet/tcp_syncache.c +++ b/sys/netinet/tcp_syncache.c @@ -96,7 +96,6 @@ #include #endif #include -#define IPSEC #endif /*FAST_IPSEC*/ #include @@ -621,6 +620,11 @@ syncache_socket(sc, lso, m) } #ifdef IPSEC /* copy old policy into new socket's */ + if (ipsec_copy_pcbpolicy(sotoinpcb(lso)->inp_sp, inp->inp_sp)) + printf("syncache_expand: could not copy policy\n"); +#endif +#ifdef FAST_IPSEC + /* copy old policy into new socket's */ if (ipsec_copy_policy(sotoinpcb(lso)->inp_sp, inp->inp_sp)) printf("syncache_expand: could not copy policy\n"); #endif diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 0eb0377..96f3640 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -109,7 +109,6 @@ #ifdef FAST_IPSEC #include #include -#define IPSEC #endif #include @@ -2218,6 +2217,10 @@ icmp6_reflect(m, off) */ m->m_flags &= ~(M_BCAST|M_MCAST); +#ifdef IPSEC + /* Don't lookup socket */ + (void)ipsec_setsocket(m, NULL); +#endif /* IPSEC */ #ifdef COMPAT_RFC1885 ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif, NULL); @@ -2441,7 +2444,7 @@ icmp6_redirect_input(m, off) sdst.sin6_len = sizeof(struct sockaddr_in6); bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst); -#ifdef IPSEC +#if defined(IPSEC) || defined(FAST_IPSEC) key_sa_routechange((struct sockaddr *)&sdst); #endif } @@ -2725,6 +2728,10 @@ noredhdropt:; sizeof(*ip6), ntohs(ip6->ip6_plen)); /* send the packet to outside... */ +#ifdef IPSEC + /* Don't lookup socket */ + (void)ipsec_setsocket(m, NULL); +#endif /* IPSEC */ ip6_output(m, NULL, NULL, 0, NULL, &outif, NULL); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 7e847e9..4f2a35c 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -109,14 +109,15 @@ ip6_forward(m, srcrt) int srcrt; { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct sockaddr_in6 *dst; - struct rtentry *rt; + struct sockaddr_in6 *dst = NULL; + struct rtentry *rt = NULL; int error, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ u_int32_t srczone, dstzone; #ifdef IPSEC struct secpolicy *sp = NULL; + int ipsecrt = 0; #endif #ifdef IPSEC @@ -253,9 +254,24 @@ ip6_forward(m, srcrt) } { + struct ipsecrequest *isr = NULL; struct ipsec_output_state state; /* + * when the kernel forwards a packet, it is not proper to apply + * IPsec transport mode to the packet is not proper. this check + * avoid from this. + * at present, if there is even a transport mode SA request in the + * security policy, the kernel does not apply IPsec to the packet. + * this check is not enough because the following case is valid. + * ipsec esp/tunnel/xxx-xxx/require esp/transport//require; + */ + for (isr = sp->req; isr; isr = isr->next) { + if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) + goto skip_ipsec; + } + + /* * All the extension headers will become inaccessible * (since they can be encrypted). * Don't panic, we need no more updates to extension headers @@ -300,10 +316,22 @@ ip6_forward(m, srcrt) m_freem(m); return; } + + /* adjust pointer */ + ip6 = mtod(m, struct ip6_hdr *); + dst = (struct sockaddr_in6 *)state.dst; + rt = state.ro ? state.ro->ro_rt : NULL; + if (dst != NULL && rt != NULL) + ipsecrt = 1; } skip_ipsec: #endif /* IPSEC */ +#ifdef IPSEC + if (ipsecrt) + goto skip_routing; +#endif + dst = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; if (!srcrt) { /* ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst */ @@ -353,6 +381,9 @@ ip6_forward(m, srcrt) } } rt = ip6_forward_rt.ro_rt; +#ifdef IPSEC + skip_routing:; +#endif /* * Scope check: if a packet can't be delivered to its destination @@ -362,8 +393,18 @@ ip6_forward(m, srcrt) * [draft-ietf-ipngwg-icmp-v3-02.txt, Section 3.1] */ if (in6_addr2zoneid(m->m_pkthdr.rcvif, &ip6->ip6_src, &srczone) || - in6_addr2zoneid(rt->rt_ifp, &ip6->ip6_src, &dstzone) || - srczone != dstzone) { + in6_addr2zoneid(rt->rt_ifp, &ip6->ip6_src, &dstzone)) { + /* XXX: this should not happen */ + ip6stat.ip6s_cantforward++; + ip6stat.ip6s_badscope++; + m_freem(m); + return; + } + if (srczone != dstzone +#ifdef IPSEC + && !ipsecrt +#endif + ) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); @@ -399,7 +440,7 @@ ip6_forward(m, srcrt) #ifdef IPSEC /* * When we do IPsec tunnel ingress, we need to play - * with if_mtu value (decrement IPsec header size + * with the link value (decrement IPsec header size * from mtu value). The code is much simpler than v4 * case, as we have the outgoing interface for * encapsulated packet as "rt->rt_ifp". @@ -439,6 +480,9 @@ ip6_forward(m, srcrt) * modified by a redirect. */ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && +#ifdef IPSEC + !ipsecrt && +#endif (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) { /* diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index bbb7a43..9665429 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -184,18 +184,21 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp, inp) struct route_in6 *ro_pmtu = NULL; int hdrsplit = 0; int needipsec = 0; +#ifdef FAST_IPSEC + int needipsectun = 0; + struct secpolicy *sp = NULL; +#endif /* FAST_IPSEC */ #ifdef IPSEC int needipsectun = 0; + struct socket *so; struct secpolicy *sp = NULL; - ip6 = mtod(m, struct ip6_hdr *); + /* for AH processing. stupid to have "socket" variable in IP layer... */ + so = ipsec_getsocket(m); + (void)ipsec_setsocket(m, NULL); #endif /* IPSEC */ -#ifdef FAST_IPSEC - int needipsectun = 0; - struct secpolicy *sp = NULL; ip6 = mtod(m, struct ip6_hdr *); -#endif /* FAST_IPSEC */ #define MAKE_EXTHDR(hp, mp) \ do { \ @@ -235,10 +238,10 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp, inp) #ifdef IPSEC /* get a security policy for this packet */ - if (inp == NULL) + if (so == NULL) sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); else - sp = ipsec6_getpolicybypcb(m, IPSEC_DIR_OUTBOUND, inp, &error); + sp = ipsec6_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); if (sp == NULL) { ipsec6stat.out_inval++; diff --git a/sys/netinet6/ipsec.c b/sys/netinet6/ipsec.c index fbff11c..4c96813 100644 --- a/sys/netinet6/ipsec.c +++ b/sys/netinet6/ipsec.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */ +/* $KAME: ipsec.c,v 1.204 2003/09/19 10:53:38 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -119,22 +119,24 @@ int ip4_esp_trans_deflev = IPSEC_LEVEL_USE; int ip4_esp_net_deflev = IPSEC_LEVEL_USE; int ip4_ah_trans_deflev = IPSEC_LEVEL_USE; int ip4_ah_net_deflev = IPSEC_LEVEL_USE; -struct secpolicy ip4_def_policy; +struct secpolicy *ip4_def_policy; int ip4_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ int ip4_esp_randpad = -1; -#ifdef SYSCTL_DECL +static int sp_cachegen = 1; /* cache generation # */ + SYSCTL_DECL(_net_inet_ipsec); #ifdef INET6 SYSCTL_DECL(_net_inet6_ipsec6); #endif -#endif /* net.inet.ipsec */ SYSCTL_STRUCT(_net_inet_ipsec, IPSECCTL_STATS, stats, CTLFLAG_RD, &ipsecstat, ipsecstat, ""); +#if 0 SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, - def_policy, CTLFLAG_RW, &ip4_def_policy.policy, 0, ""); + def_policy, CTLFLAG_RW, &ip4_def_policy->policy, 0, ""); +#endif SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, CTLFLAG_RW, &ip4_esp_trans_deflev, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, @@ -162,15 +164,17 @@ int ip6_esp_trans_deflev = IPSEC_LEVEL_USE; int ip6_esp_net_deflev = IPSEC_LEVEL_USE; int ip6_ah_trans_deflev = IPSEC_LEVEL_USE; int ip6_ah_net_deflev = IPSEC_LEVEL_USE; -struct secpolicy ip6_def_policy; +struct secpolicy *ip6_def_policy; int ip6_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ int ip6_esp_randpad = -1; /* net.inet6.ipsec6 */ SYSCTL_STRUCT(_net_inet6_ipsec6, IPSECCTL_STATS, stats, CTLFLAG_RD, &ipsec6stat, ipsecstat, ""); +#if 0 SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, - def_policy, CTLFLAG_RW, &ip6_def_policy.policy, 0, ""); + def_policy, CTLFLAG_RW, &ip6_def_policy->policy, 0, ""); +#endif SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, CTLFLAG_RW, &ip6_esp_trans_deflev, 0, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, @@ -187,12 +191,13 @@ SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ESP_RANDPAD, esp_randpad, CTLFLAG_RW, &ip6_esp_randpad, 0, ""); #endif /* INET6 */ +static struct secpolicy *ipsec_checkpcbcache __P((struct mbuf *, + struct inpcbpolicy *, int)); +static int ipsec_fillpcbcache __P((struct inpcbpolicy *, struct mbuf *, + struct secpolicy *, int)); +static int ipsec_invalpcbcache __P((struct inpcbpolicy *, int)); static int ipsec_setspidx_mbuf - __P((struct secpolicyindex *, u_int, u_int, struct mbuf *, int)); -static int ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *)); -#ifdef INET6 -static int ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *)); -#endif + __P((struct secpolicyindex *, int, struct mbuf *, int)); static int ipsec_setspidx __P((struct mbuf *, struct secpolicyindex *, int)); static void ipsec4_get_ulp __P((struct mbuf *, struct secpolicyindex *, int)); static int ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); @@ -202,6 +207,9 @@ static int ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); #endif static struct inpcbpolicy *ipsec_newpcbpolicy __P((void)); static void ipsec_delpcbpolicy __P((struct inpcbpolicy *)); +#if 0 +static int ipsec_deepcopy_pcbpolicy __P((struct inpcbpolicy *)); +#endif static struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *)); static int ipsec_set_policy __P((struct secpolicy **, int, caddr_t, size_t, int)); @@ -221,6 +229,172 @@ static int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *)); #ifdef INET6 static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *)); #endif +static struct ipsecaux *ipsec_addaux __P((struct mbuf *)); +static struct ipsecaux *ipsec_findaux __P((struct mbuf *)); +static void ipsec_optaux __P((struct mbuf *, struct ipsecaux *)); +#ifdef INET +static int ipsec4_checksa __P((struct ipsecrequest *, + struct ipsec_output_state *)); +#endif +#ifdef INET6 +static int ipsec6_checksa __P((struct ipsecrequest *, + struct ipsec_output_state *, int)); +#endif + +/* + * try to validate and use cached policy on a pcb. + */ +static struct secpolicy * +ipsec_checkpcbcache(m, pcbsp, dir) + struct mbuf *m; + struct inpcbpolicy *pcbsp; + int dir; +{ + struct secpolicyindex spidx; + struct timeval mono_time; + + microtime(&mono_time); + + switch (dir) { + case IPSEC_DIR_INBOUND: + case IPSEC_DIR_OUTBOUND: + case IPSEC_DIR_ANY: + break; + default: + return NULL; + } +#ifdef DIAGNOSTIC + if (dir >= sizeof(pcbsp->cache)/sizeof(pcbsp->cache[0])) + panic("dir too big in ipsec_checkpcbcache"); +#endif + /* SPD table change invalidates all the caches */ + if (pcbsp->cachegen[dir] == 0 || sp_cachegen > pcbsp->cachegen[dir]) { + ipsec_invalpcbcache(pcbsp, dir); + return NULL; + } + if (!pcbsp->cache[dir]) + return NULL; + if (pcbsp->cache[dir]->state != IPSEC_SPSTATE_ALIVE) { + ipsec_invalpcbcache(pcbsp, dir); + return NULL; + } + if ((pcbsp->cacheflags & IPSEC_PCBSP_CONNECTED) == 0) { + if (!pcbsp->cache[dir]) + return NULL; + if (ipsec_setspidx(m, &spidx, 1) != 0) + return NULL; + if (bcmp(&pcbsp->cacheidx[dir], &spidx, sizeof(spidx))) { + if (!pcbsp->cache[dir]->spidx || + !key_cmpspidx_withmask(pcbsp->cache[dir]->spidx, + &spidx)) + return NULL; + pcbsp->cacheidx[dir] = spidx; + } + } else { + /* + * The pcb is connected, and the L4 code is sure that: + * - outgoing side uses inp_[lf]addr + * - incoming side looks up policy after inpcb lookup + * and address pair is known to be stable. We do not need + * to generate spidx again, nor check the address match again. + * + * For IPv4/v6 SOCK_STREAM sockets, this assumption holds + * and there are calls to ipsec_pcbconn() from in_pcbconnect(). + */ + } + + pcbsp->cache[dir]->lastused = mono_time.tv_sec; + pcbsp->cache[dir]->refcnt++; + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec_checkpcbcache cause refcnt++:%d SP:%p\n", + pcbsp->cache[dir]->refcnt, pcbsp->cache[dir])); + return pcbsp->cache[dir]; +} + +static int +ipsec_fillpcbcache(pcbsp, m, sp, dir) + struct inpcbpolicy *pcbsp; + struct mbuf *m; + struct secpolicy *sp; + int dir; +{ + + switch (dir) { + case IPSEC_DIR_INBOUND: + case IPSEC_DIR_OUTBOUND: + break; + default: + return EINVAL; + } +#ifdef DIAGNOSTIC + if (dir >= sizeof(pcbsp->cache)/sizeof(pcbsp->cache[0])) + panic("dir too big in ipsec_checkpcbcache"); +#endif + + if (pcbsp->cache[dir]) + key_freesp(pcbsp->cache[dir]); + pcbsp->cache[dir] = NULL; + if (ipsec_setspidx(m, &pcbsp->cacheidx[dir], 1) != 0) { + return EINVAL; + } + pcbsp->cache[dir] = sp; + if (pcbsp->cache[dir]) { + pcbsp->cache[dir]->refcnt++; + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec_fillpcbcache cause refcnt++:%d SP:%p\n", + pcbsp->cache[dir]->refcnt, pcbsp->cache[dir])); + } + pcbsp->cachegen[dir] = sp_cachegen; + + return 0; +} + +static int +ipsec_invalpcbcache(pcbsp, dir) + struct inpcbpolicy *pcbsp; + int dir; +{ + int i; + + for (i = IPSEC_DIR_INBOUND; i <= IPSEC_DIR_OUTBOUND; i++) { + if (dir != IPSEC_DIR_ANY && i != dir) + continue; + if (pcbsp->cache[i]) + key_freesp(pcbsp->cache[i]); + pcbsp->cache[i] = NULL; + pcbsp->cachegen[i] = 0; + bzero(&pcbsp->cacheidx[i], sizeof(pcbsp->cacheidx[i])); + } + return 0; +} + +int +ipsec_pcbconn(pcbsp) + struct inpcbpolicy *pcbsp; +{ + + pcbsp->cacheflags |= IPSEC_PCBSP_CONNECTED; + ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY); + return 0; +} + +int +ipsec_pcbdisconn(pcbsp) + struct inpcbpolicy *pcbsp; +{ + + pcbsp->cacheflags &= ~IPSEC_PCBSP_CONNECTED; + ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY); + return 0; +} + +int +ipsec_invalpcbcacheall() +{ + + sp_cachegen++; + return 0; +} /* * For OUTBOUND packet having a socket. Searching SPD for packet, @@ -235,34 +409,50 @@ static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *)); * NOTE: IPv6 mapped adddress concern is implemented here. */ struct secpolicy * -ipsec4_getpolicybypcb(m, dir, inp, error) +ipsec4_getpolicybysock(m, dir, so, error) struct mbuf *m; u_int dir; - struct inpcb *inp; + struct socket *so; int *error; { struct inpcbpolicy *pcbsp = NULL; struct secpolicy *currsp = NULL; /* policy on socket */ struct secpolicy *kernsp = NULL; /* policy on kernel */ + struct secpolicyindex spidx; + u_int16_t tag; /* sanity check */ - if (m == NULL || inp == NULL || error == NULL) + if (m == NULL || so == NULL || error == NULL) panic("ipsec4_getpolicybysock: NULL pointer was passed."); - /* set spidx in pcb */ + switch (so->so_proto->pr_domain->dom_family) { + case AF_INET: + pcbsp = sotoinpcb(so)->inp_sp; + break; #ifdef INET6 - if (inp->inp_vflag & INP_IPV6PROTO) - *error = ipsec6_setspidx_in6pcb(m, inp); - else + case AF_INET6: + pcbsp = sotoin6pcb(so)->in6p_sp; + break; #endif - *error = ipsec4_setspidx_inpcb(m, inp); - if (*error) - return NULL; - pcbsp = inp->inp_sp; + default: + panic("ipsec4_getpolicybysock: unsupported address family"); + } - /* sanity check */ +#ifdef DIAGNOSTIC if (pcbsp == NULL) panic("ipsec4_getpolicybysock: pcbsp is NULL."); +#endif + + tag = 0; + + /* if we have a cached entry, and if it is still valid, use it. */ + ipsecstat.spdcachelookup++; + currsp = ipsec_checkpcbcache(m, pcbsp, dir); + if (currsp) { + *error = 0; + return currsp; + } + ipsecstat.spdcachemiss++; switch (dir) { case IPSEC_DIR_INBOUND: @@ -285,36 +475,32 @@ ipsec4_getpolicybypcb(m, dir, inp, error) case IPSEC_POLICY_BYPASS: currsp->refcnt++; *error = 0; + ipsec_fillpcbcache(pcbsp, m, currsp, dir); return currsp; case IPSEC_POLICY_ENTRUST: /* look for a policy in SPD */ - kernsp = key_allocsp(&currsp->spidx, dir); - - /* SP found */ - if (kernsp != NULL) { + if (ipsec_setspidx_mbuf(&spidx, AF_INET, m, 1) == 0 && + (kernsp = key_allocsp(tag, &spidx, dir)) != NULL) { + /* SP found */ KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec4_getpolicybysock called " "to allocate SP:%p\n", kernsp)); *error = 0; + ipsec_fillpcbcache(pcbsp, m, kernsp, dir); return kernsp; } /* no SP found */ - if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD - && ip4_def_policy.policy != IPSEC_POLICY_NONE) { - ipseclog((LOG_INFO, - "fixed system default policy: %d->%d\n", - ip4_def_policy.policy, IPSEC_POLICY_NONE)); - ip4_def_policy.policy = IPSEC_POLICY_NONE; - } - ip4_def_policy.refcnt++; + ip4_def_policy->refcnt++; *error = 0; - return &ip4_def_policy; - + ipsec_fillpcbcache(pcbsp, m, ip4_def_policy, dir); + return ip4_def_policy; + case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; + ipsec_fillpcbcache(pcbsp, m, currsp, dir); return currsp; default: @@ -328,14 +514,14 @@ ipsec4_getpolicybypcb(m, dir, inp, error) /* when non-privilieged socket */ /* look for a policy in SPD */ - kernsp = key_allocsp(&currsp->spidx, dir); - - /* SP found */ - if (kernsp != NULL) { + if (ipsec_setspidx_mbuf(&spidx, AF_INET, m, 1) == 0 && + (kernsp = key_allocsp(tag, &spidx, dir)) != NULL) { + /* SP found */ KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec4_getpolicybysock called " "to allocate SP:%p\n", kernsp)); *error = 0; + ipsec_fillpcbcache(pcbsp, m, kernsp, dir); return kernsp; } @@ -349,20 +535,15 @@ ipsec4_getpolicybypcb(m, dir, inp, error) return NULL; case IPSEC_POLICY_ENTRUST: - if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD - && ip4_def_policy.policy != IPSEC_POLICY_NONE) { - ipseclog((LOG_INFO, - "fixed system default policy: %d->%d\n", - ip4_def_policy.policy, IPSEC_POLICY_NONE)); - ip4_def_policy.policy = IPSEC_POLICY_NONE; - } - ip4_def_policy.refcnt++; + ip4_def_policy->refcnt++; *error = 0; - return &ip4_def_policy; + ipsec_fillpcbcache(pcbsp, m, ip4_def_policy, dir); + return ip4_def_policy; case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; + ipsec_fillpcbcache(pcbsp, m, currsp, dir); return currsp; default: @@ -374,19 +555,6 @@ ipsec4_getpolicybypcb(m, dir, inp, error) /* NOTREACHED */ } -struct secpolicy * -ipsec4_getpolicybysock(m, dir, so, error) - struct mbuf *m; - u_int dir; - struct socket *so; - int *error; -{ - - if (so == NULL) - panic("ipsec4_getpolicybysock: NULL pointer was passed."); - return (ipsec4_getpolicybypcb(m, dir, sotoinpcb(so), error)); -} - /* * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, * and return a pointer to SP. @@ -405,24 +573,28 @@ ipsec4_getpolicybyaddr(m, dir, flag, error) int *error; { struct secpolicy *sp = NULL; + u_int16_t tag; /* sanity check */ if (m == NULL || error == NULL) panic("ipsec4_getpolicybyaddr: NULL pointer was passed."); + /* get a policy entry matched with the packet */ { struct secpolicyindex spidx; bzero(&spidx, sizeof(spidx)); /* make an index to look for a policy */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m, + *error = ipsec_setspidx_mbuf(&spidx, AF_INET, m, (flag & IP_FORWARDING) ? 0 : 1); if (*error != 0) return NULL; - sp = key_allocsp(&spidx, dir); + tag = 0; + + sp = key_allocsp(tag, &spidx, dir); } /* SP found */ @@ -435,15 +607,9 @@ ipsec4_getpolicybyaddr(m, dir, flag, error) } /* no SP found */ - if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD && - ip4_def_policy.policy != IPSEC_POLICY_NONE) { - ipseclog((LOG_INFO, "fixed system default policy:%d->%d\n", - ip4_def_policy.policy, IPSEC_POLICY_NONE)); - ip4_def_policy.policy = IPSEC_POLICY_NONE; - } - ip4_def_policy.refcnt++; + ip4_def_policy->refcnt++; *error = 0; - return &ip4_def_policy; + return ip4_def_policy; } #ifdef INET6 @@ -458,32 +624,44 @@ ipsec4_getpolicybyaddr(m, dir, flag, error) * others: a pointer to SP */ struct secpolicy * -ipsec6_getpolicybypcb(m, dir, inp, error) +ipsec6_getpolicybysock(m, dir, so, error) struct mbuf *m; u_int dir; - struct inpcb *inp; + struct socket *so; int *error; { struct inpcbpolicy *pcbsp = NULL; struct secpolicy *currsp = NULL; /* policy on socket */ struct secpolicy *kernsp = NULL; /* policy on kernel */ + struct secpolicyindex spidx; + u_int16_t tag; /* sanity check */ - if (m == NULL || inp == NULL || error == NULL) + if (m == NULL || so == NULL || error == NULL) panic("ipsec6_getpolicybysock: NULL pointer was passed."); #ifdef DIAGNOSTIC - if ((inp->inp_vflag & INP_IPV6PROTO) == 0) + if (so->so_proto->pr_domain->dom_family != AF_INET6) panic("ipsec6_getpolicybysock: socket domain != inet6"); #endif - /* set spidx in pcb */ - ipsec6_setspidx_in6pcb(m, inp); - pcbsp = inp->in6p_sp; + pcbsp = sotoin6pcb(so)->in6p_sp; - /* sanity check */ +#ifdef DIAGNOSTIC if (pcbsp == NULL) panic("ipsec6_getpolicybysock: pcbsp is NULL."); +#endif + + tag = 0; + + /* if we have a cached entry, and if it is still valid, use it. */ + ipsec6stat.spdcachelookup++; + currsp = ipsec_checkpcbcache(m, pcbsp, dir); + if (currsp) { + *error = 0; + return currsp; + } + ipsec6stat.spdcachemiss++; switch (dir) { case IPSEC_DIR_INBOUND: @@ -506,36 +684,32 @@ ipsec6_getpolicybypcb(m, dir, inp, error) case IPSEC_POLICY_BYPASS: currsp->refcnt++; *error = 0; + ipsec_fillpcbcache(pcbsp, m, currsp, dir); return currsp; case IPSEC_POLICY_ENTRUST: /* look for a policy in SPD */ - kernsp = key_allocsp(&currsp->spidx, dir); - - /* SP found */ - if (kernsp != NULL) { + if (ipsec_setspidx_mbuf(&spidx, AF_INET6, m, 1) == 0 && + (kernsp = key_allocsp(tag, &spidx, dir)) != NULL) { + /* SP found */ KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec6_getpolicybysock called " "to allocate SP:%p\n", kernsp)); *error = 0; + ipsec_fillpcbcache(pcbsp, m, kernsp, dir); return kernsp; } /* no SP found */ - if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD - && ip6_def_policy.policy != IPSEC_POLICY_NONE) { - ipseclog((LOG_INFO, - "fixed system default policy: %d->%d\n", - ip6_def_policy.policy, IPSEC_POLICY_NONE)); - ip6_def_policy.policy = IPSEC_POLICY_NONE; - } - ip6_def_policy.refcnt++; + ip6_def_policy->refcnt++; *error = 0; - return &ip6_def_policy; - + ipsec_fillpcbcache(pcbsp, m, ip6_def_policy, dir); + return ip6_def_policy; + case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; + ipsec_fillpcbcache(pcbsp, m, currsp, dir); return currsp; default: @@ -549,14 +723,14 @@ ipsec6_getpolicybypcb(m, dir, inp, error) /* when non-privilieged socket */ /* look for a policy in SPD */ - kernsp = key_allocsp(&currsp->spidx, dir); - - /* SP found */ - if (kernsp != NULL) { + if (ipsec_setspidx_mbuf(&spidx, AF_INET6, m, 1) == 0 && + (kernsp = key_allocsp(tag, &spidx, dir)) != NULL) { + /* SP found */ KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec6_getpolicybysock called " "to allocate SP:%p\n", kernsp)); *error = 0; + ipsec_fillpcbcache(pcbsp, m, kernsp, dir); return kernsp; } @@ -570,20 +744,15 @@ ipsec6_getpolicybypcb(m, dir, inp, error) return NULL; case IPSEC_POLICY_ENTRUST: - if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD - && ip6_def_policy.policy != IPSEC_POLICY_NONE) { - ipseclog((LOG_INFO, - "fixed system default policy: %d->%d\n", - ip6_def_policy.policy, IPSEC_POLICY_NONE)); - ip6_def_policy.policy = IPSEC_POLICY_NONE; - } - ip6_def_policy.refcnt++; + ip6_def_policy->refcnt++; *error = 0; - return &ip6_def_policy; + ipsec_fillpcbcache(pcbsp, m, ip6_def_policy, dir); + return ip6_def_policy; case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; + ipsec_fillpcbcache(pcbsp, m, currsp, dir); return currsp; default: @@ -596,19 +765,6 @@ ipsec6_getpolicybypcb(m, dir, inp, error) /* NOTREACHED */ } -struct secpolicy * -ipsec6_getpolicybysock(m, dir, so, error) - struct mbuf *m; - u_int dir; - struct socket *so; - int *error; -{ - - if (so == NULL) - panic("ipsec6_getpolicybysock: NULL pointer was passed."); - return (ipsec6_getpolicybypcb(m, dir, sotoin6pcb(so), error)); -} - /* * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, * and return a pointer to SP. @@ -633,24 +789,28 @@ ipsec6_getpolicybyaddr(m, dir, flag, error) int *error; { struct secpolicy *sp = NULL; + u_int16_t tag; /* sanity check */ if (m == NULL || error == NULL) panic("ipsec6_getpolicybyaddr: NULL pointer was passed."); + /* get a policy entry matched with the packet */ { struct secpolicyindex spidx; bzero(&spidx, sizeof(spidx)); /* make an index to look for a policy */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m, + *error = ipsec_setspidx_mbuf(&spidx, AF_INET6, m, (flag & IP_FORWARDING) ? 0 : 1); if (*error != 0) return NULL; - sp = key_allocsp(&spidx, dir); + tag = 0; + + sp = key_allocsp(tag, &spidx, dir); } /* SP found */ @@ -663,15 +823,9 @@ ipsec6_getpolicybyaddr(m, dir, flag, error) } /* no SP found */ - if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD - && ip6_def_policy.policy != IPSEC_POLICY_NONE) { - ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n", - ip6_def_policy.policy, IPSEC_POLICY_NONE)); - ip6_def_policy.policy = IPSEC_POLICY_NONE; - } - ip6_def_policy.refcnt++; + ip6_def_policy->refcnt++; *error = 0; - return &ip6_def_policy; + return ip6_def_policy; } #endif /* INET6 */ @@ -686,9 +840,9 @@ ipsec6_getpolicybyaddr(m, dir, flag, error) * other: failure, and set errno. */ int -ipsec_setspidx_mbuf(spidx, dir, family, m, needport) +ipsec_setspidx_mbuf(spidx, family, m, needport) struct secpolicyindex *spidx; - u_int dir, family; + int family; struct mbuf *m; int needport; { @@ -703,7 +857,6 @@ ipsec_setspidx_mbuf(spidx, dir, family, m, needport) error = ipsec_setspidx(m, spidx, needport); if (error) goto bad; - spidx->dir = dir; return 0; @@ -713,86 +866,6 @@ ipsec_setspidx_mbuf(spidx, dir, family, m, needport) return EINVAL; } -static int -ipsec4_setspidx_inpcb(m, pcb) - struct mbuf *m; - struct inpcb *pcb; -{ - struct secpolicyindex *spidx; - int error; - - /* sanity check */ - if (pcb == NULL) - panic("ipsec4_setspidx_inpcb: no PCB found."); - if (pcb->inp_sp == NULL) - panic("ipsec4_setspidx_inpcb: no inp_sp found."); - if (pcb->inp_sp->sp_out == NULL || pcb->inp_sp->sp_in == NULL) - panic("ipsec4_setspidx_inpcb: no sp_in/out found."); - - bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); - bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); - - spidx = &pcb->inp_sp->sp_in->spidx; - error = ipsec_setspidx(m, spidx, 1); - if (error) - goto bad; - spidx->dir = IPSEC_DIR_INBOUND; - - spidx = &pcb->inp_sp->sp_out->spidx; - error = ipsec_setspidx(m, spidx, 1); - if (error) - goto bad; - spidx->dir = IPSEC_DIR_OUTBOUND; - - return 0; - -bad: - bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); - bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); - return error; -} - -#ifdef INET6 -static int -ipsec6_setspidx_in6pcb(m, pcb) - struct mbuf *m; - struct in6pcb *pcb; -{ - struct secpolicyindex *spidx; - int error; - - /* sanity check */ - if (pcb == NULL) - panic("ipsec6_setspidx_in6pcb: no PCB found."); - if (pcb->in6p_sp == NULL) - panic("ipsec6_setspidx_in6pcb: no in6p_sp found."); - if (pcb->in6p_sp->sp_out == NULL || pcb->in6p_sp->sp_in == NULL) - panic("ipsec6_setspidx_in6pcb: no sp_in/out found."); - - bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); - bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); - - spidx = &pcb->in6p_sp->sp_in->spidx; - error = ipsec_setspidx(m, spidx, 1); - if (error) - goto bad; - spidx->dir = IPSEC_DIR_INBOUND; - - spidx = &pcb->in6p_sp->sp_out->spidx; - error = ipsec_setspidx(m, spidx, 1); - if (error) - goto bad; - spidx->dir = IPSEC_DIR_OUTBOUND; - - return 0; - -bad: - bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); - bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); - return error; -} -#endif - /* * configure security policy index (src/dst/proto/sport/dport) * by looking at the content of mbuf. @@ -814,6 +887,8 @@ ipsec_setspidx(m, spidx, needport) if (m == NULL) panic("ipsec_setspidx: m == 0 passed."); + bzero(spidx, sizeof(*spidx)); + /* * validate m->m_pkthdr.len. we see incorrect length if we * mistakenly call this function with inconsistent mbuf chain @@ -1000,6 +1075,7 @@ ipsec6_get_ulp(m, spidx, needport) int off, nxt; struct tcphdr th; struct udphdr uh; + struct icmp6_hdr ih; /* sanity check */ if (m == NULL) @@ -1040,6 +1116,15 @@ ipsec6_get_ulp(m, spidx, needport) ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport; break; case IPPROTO_ICMPV6: + spidx->ul_proto = nxt; + if (off + sizeof(struct icmp6_hdr) > m->m_pkthdr.len) + break; + m_copydata(m, off, sizeof(ih), (caddr_t)&ih); + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = + htons((u_int16_t)ih.icmp6_type); + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = + htons((u_int16_t)ih.icmp6_code); + break; default: /* XXX intermediate headers??? */ spidx->ul_proto = nxt; @@ -1068,22 +1153,14 @@ ipsec6_setspidx_ipaddr(m, spidx) bzero(sin6, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(struct sockaddr_in6); - bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src)); - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { - sin6->sin6_addr.s6_addr16[1] = 0; - sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); - } + in6_recoverscope(sin6, &ip6->ip6_src, NULL); spidx->prefs = sizeof(struct in6_addr) << 3; sin6 = (struct sockaddr_in6 *)&spidx->dst; bzero(sin6, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(struct sockaddr_in6); - bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst)); - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { - sin6->sin6_addr.s6_addr16[1] = 0; - sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); - } + in6_recoverscope(sin6, &ip6->ip6_dst, NULL); spidx->prefd = sizeof(struct in6_addr) << 3; return 0; @@ -1109,19 +1186,47 @@ ipsec_delpcbpolicy(p) /* initialize policy in PCB */ int -ipsec_init_policy(so, pcb_sp) +ipsec_init_pcbpolicy(so, pcb_sp) struct socket *so; struct inpcbpolicy **pcb_sp; { struct inpcbpolicy *new; + static int initialized = 0; + static struct secpolicy *in = NULL, *out = NULL; /* sanity check. */ if (so == NULL || pcb_sp == NULL) - panic("ipsec_init_policy: NULL pointer was passed."); + panic("ipsec_init_pcbpolicy: NULL pointer was passed."); + + if (!initialized) { + if ((in = key_newsp(0)) == NULL) + return ENOBUFS; + if ((out = key_newsp(0)) == NULL) { + key_freesp(in); + in = NULL; + return ENOBUFS; + } + + in->state = IPSEC_SPSTATE_ALIVE; + in->policy = IPSEC_POLICY_ENTRUST; + in->dir = IPSEC_DIR_INBOUND; + in->readonly = 1; + in->persist = 1; + in->so = NULL; + + out->state = IPSEC_SPSTATE_ALIVE; + out->policy = IPSEC_POLICY_ENTRUST; + out->dir = IPSEC_DIR_OUTBOUND; + out->readonly = 1; + out->persist = 1; + out->so = NULL; + + initialized++; + } new = ipsec_newpcbpolicy(); if (new == NULL) { - ipseclog((LOG_DEBUG, "ipsec_init_policy: No more memory.\n")); + ipseclog((LOG_DEBUG, "ipsec_init_pcbpolicy: No more memory.\n")); return ENOBUFS; } bzero(new, sizeof(*new)); @@ -1131,20 +1236,10 @@ ipsec_init_policy(so, pcb_sp) else new->priv = 0; - if ((new->sp_in = key_newsp()) == NULL) { - ipsec_delpcbpolicy(new); - return ENOBUFS; - } - new->sp_in->state = IPSEC_SPSTATE_ALIVE; - new->sp_in->policy = IPSEC_POLICY_ENTRUST; - - if ((new->sp_out = key_newsp()) == NULL) { - key_freesp(new->sp_in); - ipsec_delpcbpolicy(new); - return ENOBUFS; - } - new->sp_out->state = IPSEC_SPSTATE_ALIVE; - new->sp_out->policy = IPSEC_POLICY_ENTRUST; + new->sp_in = in; + new->sp_in->refcnt++; + new->sp_out = out; + new->sp_out->refcnt++; *pcb_sp = new; @@ -1153,29 +1248,57 @@ ipsec_init_policy(so, pcb_sp) /* copy old ipsec policy into new */ int -ipsec_copy_policy(old, new) +ipsec_copy_pcbpolicy(old, new) struct inpcbpolicy *old, *new; { + + if (new->sp_in) + key_freesp(new->sp_in); + if (old->sp_in->policy == IPSEC_POLICY_IPSEC) + new->sp_in = ipsec_deepcopy_policy(old->sp_in); + else { + new->sp_in = old->sp_in; + new->sp_in->refcnt++; + } + + if (new->sp_out) + key_freesp(new->sp_out); + if (old->sp_out->policy == IPSEC_POLICY_IPSEC) + new->sp_out = ipsec_deepcopy_policy(old->sp_out); + else { + new->sp_out = old->sp_out; + new->sp_out->refcnt++; + } + + new->priv = old->priv; + + return 0; +} + +#if 0 +static int +ipsec_deepcopy_pcbpolicy(pcb_sp) + struct inpcbpolicy *pcb_sp; +{ struct secpolicy *sp; - sp = ipsec_deepcopy_policy(old->sp_in); + sp = ipsec_deepcopy_policy(pcb_sp->sp_in); if (sp) { - key_freesp(new->sp_in); - new->sp_in = sp; + key_freesp(pcb_sp->sp_in); + pcb_sp->sp_in = sp; } else return ENOBUFS; - sp = ipsec_deepcopy_policy(old->sp_out); + sp = ipsec_deepcopy_policy(pcb_sp->sp_out); if (sp) { - key_freesp(new->sp_out); - new->sp_out = sp; + key_freesp(pcb_sp->sp_out); + pcb_sp->sp_out = sp; } else return ENOBUFS; - new->priv = old->priv; - return 0; } +#endif /* deep-copy a policy in PCB */ static struct secpolicy * @@ -1188,7 +1311,7 @@ ipsec_deepcopy_policy(src) struct ipsecrequest *r; struct secpolicy *dst; - dst = key_newsp(); + dst = key_newsp(0); if (src == NULL || dst == NULL) return NULL; @@ -1219,9 +1342,15 @@ ipsec_deepcopy_policy(src) q = &((*q)->next); } + if (src->spidx) + if (keydb_setsecpolicyindex(dst, src->spidx) != 0) + goto fail; + dst->req = newchain; dst->state = src->state; dst->policy = src->policy; + dst->dir = src->dir; + dst->so = src->so; /* do not touch the refcnt fields */ return dst; @@ -1232,13 +1361,14 @@ fail: free(p, M_SECA); p = NULL; } + key_freesp(dst); return NULL; } /* set policy and ipsec request if present. */ static int -ipsec_set_policy(pcb_sp, optname, request, len, priv) - struct secpolicy **pcb_sp; +ipsec_set_policy(spp, optname, request, len, priv) + struct secpolicy **spp; int optname; caddr_t request; size_t len; @@ -1249,7 +1379,7 @@ ipsec_set_policy(pcb_sp, optname, request, len, priv) int error; /* sanity check. */ - if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL) + if (spp == NULL || *spp == NULL || request == NULL) return EINVAL; if (len < sizeof(*xpl)) return EINVAL; @@ -1276,8 +1406,8 @@ ipsec_set_policy(pcb_sp, optname, request, len, priv) newsp->state = IPSEC_SPSTATE_ALIVE; /* clear old SP and set new SP */ - key_freesp(*pcb_sp); - *pcb_sp = newsp; + key_freesp(*spp); + *spp = newsp; KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_set_policy: new policy\n"); kdebug_secpolicy(newsp)); @@ -1286,16 +1416,16 @@ ipsec_set_policy(pcb_sp, optname, request, len, priv) } static int -ipsec_get_policy(pcb_sp, mp) - struct secpolicy *pcb_sp; +ipsec_get_policy(sp, mp) + struct secpolicy *sp; struct mbuf **mp; { /* sanity check. */ - if (pcb_sp == NULL || mp == NULL) + if (sp == NULL || mp == NULL) return EINVAL; - *mp = key_sp2msg(pcb_sp); + *mp = key_sp2msg(sp); if (!*mp) { ipseclog((LOG_DEBUG, "ipsec_get_policy: No more memory.\n")); return ENOBUFS; @@ -1318,7 +1448,7 @@ ipsec4_set_policy(inp, optname, request, len, priv) int priv; { struct sadb_x_policy *xpl; - struct secpolicy **pcb_sp; + struct secpolicy **spp; /* sanity check. */ if (inp == NULL || request == NULL) @@ -1330,10 +1460,10 @@ ipsec4_set_policy(inp, optname, request, len, priv) /* select direction */ switch (xpl->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: - pcb_sp = &inp->inp_sp->sp_in; + spp = &inp->inp_sp->sp_in; break; case IPSEC_DIR_OUTBOUND: - pcb_sp = &inp->inp_sp->sp_out; + spp = &inp->inp_sp->sp_out; break; default: ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n", @@ -1341,7 +1471,8 @@ ipsec4_set_policy(inp, optname, request, len, priv) return EINVAL; } - return ipsec_set_policy(pcb_sp, optname, request, len, priv); + ipsec_invalpcbcache(inp->inp_sp, IPSEC_DIR_ANY); + return ipsec_set_policy(spp, optname, request, len, priv); } int @@ -1352,7 +1483,7 @@ ipsec4_get_policy(inp, request, len, mp) struct mbuf **mp; { struct sadb_x_policy *xpl; - struct secpolicy *pcb_sp; + struct secpolicy *sp; /* sanity check. */ if (inp == NULL || request == NULL || mp == NULL) @@ -1366,10 +1497,10 @@ ipsec4_get_policy(inp, request, len, mp) /* select direction */ switch (xpl->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: - pcb_sp = inp->inp_sp->sp_in; + sp = inp->inp_sp->sp_in; break; case IPSEC_DIR_OUTBOUND: - pcb_sp = inp->inp_sp->sp_out; + sp = inp->inp_sp->sp_out; break; default: ipseclog((LOG_ERR, "ipsec4_get_policy: invalid direction=%u\n", @@ -1377,7 +1508,7 @@ ipsec4_get_policy(inp, request, len, mp) return EINVAL; } - return ipsec_get_policy(pcb_sp, mp); + return ipsec_get_policy(sp, mp); } /* delete policy in PCB */ @@ -1402,6 +1533,8 @@ ipsec4_delete_pcbpolicy(inp) inp->inp_sp->sp_out = NULL; } + ipsec_invalpcbcache(inp->inp_sp, IPSEC_DIR_ANY); + ipsec_delpcbpolicy(inp->inp_sp); inp->inp_sp = NULL; @@ -1418,7 +1551,7 @@ ipsec6_set_policy(in6p, optname, request, len, priv) int priv; { struct sadb_x_policy *xpl; - struct secpolicy **pcb_sp; + struct secpolicy **spp; /* sanity check. */ if (in6p == NULL || request == NULL) @@ -1430,10 +1563,10 @@ ipsec6_set_policy(in6p, optname, request, len, priv) /* select direction */ switch (xpl->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: - pcb_sp = &in6p->in6p_sp->sp_in; + spp = &in6p->in6p_sp->sp_in; break; case IPSEC_DIR_OUTBOUND: - pcb_sp = &in6p->in6p_sp->sp_out; + spp = &in6p->in6p_sp->sp_out; break; default: ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n", @@ -1441,7 +1574,8 @@ ipsec6_set_policy(in6p, optname, request, len, priv) return EINVAL; } - return ipsec_set_policy(pcb_sp, optname, request, len, priv); + ipsec_invalpcbcache(in6p->in6p_sp, IPSEC_DIR_ANY); + return ipsec_set_policy(spp, optname, request, len, priv); } int @@ -1452,7 +1586,7 @@ ipsec6_get_policy(in6p, request, len, mp) struct mbuf **mp; { struct sadb_x_policy *xpl; - struct secpolicy *pcb_sp; + struct secpolicy *sp; /* sanity check. */ if (in6p == NULL || request == NULL || mp == NULL) @@ -1466,10 +1600,10 @@ ipsec6_get_policy(in6p, request, len, mp) /* select direction */ switch (xpl->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: - pcb_sp = in6p->in6p_sp->sp_in; + sp = in6p->in6p_sp->sp_in; break; case IPSEC_DIR_OUTBOUND: - pcb_sp = in6p->in6p_sp->sp_out; + sp = in6p->in6p_sp->sp_out; break; default: ipseclog((LOG_ERR, "ipsec6_get_policy: invalid direction=%u\n", @@ -1477,7 +1611,7 @@ ipsec6_get_policy(in6p, request, len, mp) return EINVAL; } - return ipsec_get_policy(pcb_sp, mp); + return ipsec_get_policy(sp, mp); } int @@ -1501,6 +1635,8 @@ ipsec6_delete_pcbpolicy(in6p) in6p->in6p_sp->sp_out = NULL; } + ipsec_invalpcbcache(in6p->in6p_sp, IPSEC_DIR_ANY); + ipsec_delpcbpolicy(in6p->in6p_sp); in6p->in6p_sp = NULL; @@ -1513,8 +1649,9 @@ ipsec6_delete_pcbpolicy(in6p) * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. */ u_int -ipsec_get_reqlevel(isr) +ipsec_get_reqlevel(isr, af) struct ipsecrequest *isr; + int af; { u_int level = 0; u_int esp_trans_deflev, esp_net_deflev, ah_trans_deflev, ah_net_deflev; @@ -1522,47 +1659,30 @@ ipsec_get_reqlevel(isr) /* sanity check */ if (isr == NULL || isr->sp == NULL) panic("ipsec_get_reqlevel: NULL pointer is passed."); - if (((struct sockaddr *)&isr->sp->spidx.src)->sa_family - != ((struct sockaddr *)&isr->sp->spidx.dst)->sa_family) - panic("ipsec_get_reqlevel: family mismatched."); - -/* XXX note that we have ipseclog() expanded here - code sync issue */ -#define IPSEC_CHECK_DEFAULT(lev) \ - (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \ - && (lev) != IPSEC_LEVEL_UNIQUE) \ - ? (ipsec_debug \ - ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\ - (lev), IPSEC_LEVEL_REQUIRE) \ - : 0), \ - (lev) = IPSEC_LEVEL_REQUIRE, \ - (lev) \ - : (lev)) /* set default level */ - switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) { + switch (af) { #ifdef INET case AF_INET: - esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_trans_deflev); - esp_net_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_net_deflev); - ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_trans_deflev); - ah_net_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_net_deflev); + esp_trans_deflev = ip4_esp_trans_deflev; + esp_net_deflev = ip4_esp_net_deflev; + ah_trans_deflev = ip4_ah_trans_deflev; + ah_net_deflev = ip4_ah_net_deflev; break; #endif #ifdef INET6 case AF_INET6: - esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_trans_deflev); - esp_net_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_net_deflev); - ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_trans_deflev); - ah_net_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_net_deflev); + esp_trans_deflev = ip6_esp_trans_deflev; + esp_net_deflev = ip6_esp_net_deflev; + ah_trans_deflev = ip6_ah_trans_deflev; + ah_net_deflev = ip6_ah_net_deflev; break; #endif /* INET6 */ default: panic("key_get_reqlevel: Unknown family. %d", - ((struct sockaddr *)&isr->sp->spidx.src)->sa_family); + ((struct sockaddr *)&isr->sp->spidx->src)->sa_family); } -#undef IPSEC_CHECK_DEFAULT - /* set level */ switch (isr->level) { case IPSEC_LEVEL_DEFAULT: @@ -1587,7 +1707,7 @@ ipsec_get_reqlevel(isr) break; default: panic("ipsec_get_reqlevel: " - "Illegal protocol defined %u", + "Illegal protocol defined %u\n", isr->saidx.proto); } break; @@ -1640,7 +1760,7 @@ ipsec_in_reject(sp, m) case IPSEC_POLICY_ENTRUST: default: - panic("ipsec_hdrsiz: Invalid policy found. %d", sp->policy); + panic("ipsec_in_reject: Invalid policy found. %d", sp->policy); } need_auth = 0; @@ -1651,7 +1771,7 @@ ipsec_in_reject(sp, m) for (isr = sp->req; isr != NULL; isr = isr->next) { /* get current level */ - level = ipsec_get_reqlevel(isr); + level = ipsec_get_reqlevel(isr, AF_INET); switch (isr->saidx.proto) { case IPPROTO_ESP: @@ -1698,9 +1818,9 @@ ipsec_in_reject(sp, m) * and {ah,esp}4_input for tunnel mode */ int -ipsec4_in_reject(m, inp) +ipsec4_in_reject_so(m, so) struct mbuf *m; - struct inpcb *inp; + struct socket *so; { struct secpolicy *sp = NULL; int error; @@ -1714,11 +1834,11 @@ ipsec4_in_reject(m, inp) * When we are called from ip_forward(), we call * ipsec4_getpolicybyaddr() with IP_FORWARDING flag. */ - if (inp == NULL) + if (so == NULL) sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); else - sp = ipsec4_getpolicybypcb(m, IPSEC_DIR_INBOUND, inp, &error); + sp = ipsec4_getpolicybysock(m, IPSEC_DIR_INBOUND, so, &error); /* XXX should be panic ? -> No, there may be error. */ if (sp == NULL) @@ -1733,13 +1853,16 @@ ipsec4_in_reject(m, inp) } int -ipsec4_in_reject_so(m, so) +ipsec4_in_reject(m, inp) struct mbuf *m; - struct socket *so; + struct inpcb *inp; { - if (so == NULL) - return ipsec4_in_reject(m, NULL); - return ipsec4_in_reject(m, sotoinpcb(so)); + if (inp == NULL) + return ipsec4_in_reject_so(m, NULL); + if (inp->inp_socket) + return ipsec4_in_reject_so(m, inp->inp_socket); + else + panic("ipsec4_in_reject: invalid inpcb/socket"); } #ifdef INET6 @@ -1749,9 +1872,9 @@ ipsec4_in_reject_so(m, so) * and {ah,esp}6_input for tunnel mode */ int -ipsec6_in_reject(m, in6p) +ipsec6_in_reject_so(m, so) struct mbuf *m; - struct in6pcb *in6p; + struct socket *so; { struct secpolicy *sp = NULL; int error; @@ -1765,31 +1888,34 @@ ipsec6_in_reject(m, in6p) * When we are called from ip_forward(), we call * ipsec6_getpolicybyaddr() with IP_FORWARDING flag. */ - if (in6p == NULL) + if (so == NULL) sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); else - sp = ipsec6_getpolicybypcb(m, IPSEC_DIR_INBOUND, in6p, &error); + sp = ipsec6_getpolicybysock(m, IPSEC_DIR_INBOUND, so, &error); if (sp == NULL) return 0; /* XXX should be panic ? */ result = ipsec_in_reject(sp, m); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ipsec6_in_reject call free SP:%p\n", sp)); + printf("DP ipsec6_in_reject_so call free SP:%p\n", sp)); key_freesp(sp); return result; } int -ipsec6_in_reject_so(m, so) +ipsec6_in_reject(m, in6p) struct mbuf *m; - struct socket *so; + struct in6pcb *in6p; { - if (so == NULL) - return ipsec6_in_reject(m, NULL); - return ipsec6_in_reject(m, sotoin6pcb(so)); + if (in6p == NULL) + return ipsec6_in_reject_so(m, NULL); + if (in6p->in6p_socket) + return ipsec6_in_reject_so(m, in6p->in6p_socket); + else + panic("ipsec6_in_reject: invalid in6p/socket"); } #endif @@ -1883,11 +2009,8 @@ ipsec4_hdrsiz(m, dir, inp) /* sanity check */ if (m == NULL) return 0; /* XXX should be panic ? */ -#if 0 - /* this is possible in TIME_WAIT state */ if (inp != NULL && inp->inp_socket == NULL) panic("ipsec4_hdrsize: why is socket NULL but there is PCB."); -#endif /* get SP for this packet. * When we are called from ip_forward(), we call @@ -1896,7 +2019,7 @@ ipsec4_hdrsiz(m, dir, inp) if (inp == NULL) sp = ipsec4_getpolicybyaddr(m, dir, IP_FORWARDING, &error); else - sp = ipsec4_getpolicybypcb(m, dir, inp, &error); + sp = ipsec4_getpolicybysock(m, dir, inp->inp_socket, &error); if (sp == NULL) return 0; /* XXX should be panic ? */ @@ -1927,19 +2050,16 @@ ipsec6_hdrsiz(m, dir, in6p) /* sanity check */ if (m == NULL) - return 0; /* XXX shoud be panic ? */ -#if 0 - /* this is possible in TIME_WAIT state */ + return 0; /* XXX should be panic ? */ if (in6p != NULL && in6p->in6p_socket == NULL) panic("ipsec6_hdrsize: why is socket NULL but there is PCB."); -#endif /* get SP for this packet */ /* XXX Is it right to call with IP_FORWARDING. */ if (in6p == NULL) sp = ipsec6_getpolicybyaddr(m, dir, IP_FORWARDING, &error); else - sp = ipsec6_getpolicybypcb(m, dir, in6p, &error); + sp = ipsec6_getpolicybysock(m, dir, in6p->in6p_socket, &error); if (sp == NULL) return 0; @@ -2147,10 +2267,10 @@ ipsec6_encapsulate(m, sav) /* ip6->ip6_plen will be updated in ip6_output() */ } ip6->ip6_nxt = IPPROTO_IPV6; - bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.src)->sin6_addr, - &ip6->ip6_src, sizeof(ip6->ip6_src)); - bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.dst)->sin6_addr, - &ip6->ip6_dst, sizeof(ip6->ip6_dst)); + in6_embedscope(&ip6->ip6_src, + (struct sockaddr_in6 *)&sav->sah->saidx.src, NULL, NULL); + in6_embedscope(&ip6->ip6_dst, + (struct sockaddr_in6 *)&sav->sah->saidx.dst, NULL, NULL); ip6->ip6_hlim = IPV6_DEFHLIM; /* XXX Should ip6_src be updated later ? */ @@ -2237,7 +2357,7 @@ ipsec_updatereplay(seq, sav) struct secasvar *sav; { struct secreplay *replay; - u_int32_t diff; + u_int64_t diff; int fr; u_int32_t wsizeb; /* constant: bits of window size */ int frlast; /* constant: last frame */ @@ -2306,7 +2426,7 @@ ipsec_updatereplay(seq, sav) } ok: - if (replay->count == ~0) { + if (replay->count == 0xffffffff) { /* set overflow flag */ replay->overflow++; @@ -2344,7 +2464,7 @@ vshiftl(bitmap, nbit, wsize) for (i = 1; i < wsize; i++) { over = (bitmap[i] >> (8 - s)); bitmap[i] <<= s; - bitmap[i-1] |= over; + bitmap[i - 1] |= over; } } @@ -2477,6 +2597,37 @@ ipsec_dumpmbuf(m) } #ifdef INET +static int +ipsec4_checksa(isr, state) + struct ipsecrequest *isr; + struct ipsec_output_state *state; +{ + struct ip *ip; + struct secasindex saidx; + struct sockaddr_in *sin; + + /* make SA index for search proper SA */ + ip = mtod(state->m, struct ip *); + bcopy(&isr->saidx, &saidx, sizeof(saidx)); + saidx.mode = isr->saidx.mode; + saidx.reqid = isr->saidx.reqid; + sin = (struct sockaddr_in *)&saidx.src; + if (sin->sin_len == 0) { + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = IPSEC_PORT_ANY; + bcopy(&ip->ip_src, &sin->sin_addr, sizeof(sin->sin_addr)); + } + sin = (struct sockaddr_in *)&saidx.dst; + if (sin->sin_len == 0) { + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = IPSEC_PORT_ANY; + bcopy(&ip->ip_dst, &sin->sin_addr, sizeof(sin->sin_addr)); + } + + return key_checkrequest(isr, &saidx); +} /* * IPsec output logic for IPv4. */ @@ -2488,11 +2639,9 @@ ipsec4_output(state, sp, flags) { struct ip *ip = NULL; struct ipsecrequest *isr = NULL; - struct secasindex saidx; int s; int error; struct sockaddr_in *dst4; - struct sockaddr_in *sin; if (!state) panic("state == NULL in ipsec4_output"); @@ -2502,6 +2651,7 @@ ipsec4_output(state, sp, flags) panic("state->ro == NULL in ipsec4_output"); if (!state->dst) panic("state->dst == NULL in ipsec4_output"); + state->encap = 0; KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("ipsec4_output: applyed SP\n"); @@ -2519,30 +2669,8 @@ ipsec4_output(state, sp, flags) && (flags & IP_FORWARDING)) continue; #endif - - /* make SA index for search proper SA */ - ip = mtod(state->m, struct ip *); - bcopy(&isr->saidx, &saidx, sizeof(saidx)); - saidx.mode = isr->saidx.mode; - saidx.reqid = isr->saidx.reqid; - sin = (struct sockaddr_in *)&saidx.src; - if (sin->sin_len == 0) { - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_port = IPSEC_PORT_ANY; - bcopy(&ip->ip_src, &sin->sin_addr, - sizeof(sin->sin_addr)); - } - sin = (struct sockaddr_in *)&saidx.dst; - if (sin->sin_len == 0) { - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_port = IPSEC_PORT_ANY; - bcopy(&ip->ip_dst, &sin->sin_addr, - sizeof(sin->sin_addr)); - } - - if ((error = key_checkrequest(isr, &saidx)) != 0) { + error = ipsec4_checksa(isr, state); + if (error != 0) { /* * IPsec processing is required, but no SA found. * I assume that key_acquire() had been called @@ -2556,7 +2684,7 @@ ipsec4_output(state, sp, flags) /* validity check */ if (isr->sav == NULL) { - switch (ipsec_get_reqlevel(isr)) { + switch (ipsec_get_reqlevel(isr, AF_INET)) { case IPSEC_LEVEL_USE: continue; case IPSEC_LEVEL_REQUIRE: @@ -2639,6 +2767,8 @@ ipsec4_output(state, sp, flags) state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; dst4 = (struct sockaddr_in *)state->dst; } + + state->encap++; } else splx(s); @@ -2700,6 +2830,52 @@ bad: #endif #ifdef INET6 +static int +ipsec6_checksa(isr, state, tunnel) + struct ipsecrequest *isr; + struct ipsec_output_state *state; + int tunnel; +{ + struct ip6_hdr *ip6; + struct secasindex saidx; + struct sockaddr_in6 *sin6; + + if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { +#ifdef DIAGNOSTIC + if (!tunnel) + panic("ipsec6_checksa/inconsistent tunnel attribute"); +#endif + /* When tunnel mode, SA peers must be specified. */ + return key_checkrequest(isr, &isr->saidx); + } + + /* make SA index for search proper SA */ + ip6 = mtod(state->m, struct ip6_hdr *); + if (tunnel) { + bzero(&saidx, sizeof(saidx)); + saidx.proto = isr->saidx.proto; + } else + bcopy(&isr->saidx, &saidx, sizeof(saidx)); + saidx.mode = isr->saidx.mode; + saidx.reqid = isr->saidx.reqid; + sin6 = (struct sockaddr_in6 *)&saidx.src; + if (sin6->sin6_len == 0 || tunnel) { + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = IPSEC_PORT_ANY; + in6_recoverscope(sin6, &ip6->ip6_src, NULL); + } + sin6 = (struct sockaddr_in6 *)&saidx.dst; + if (sin6->sin6_len == 0 || tunnel) { + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = IPSEC_PORT_ANY; + in6_recoverscope(sin6, &ip6->ip6_dst, NULL); + } + + return key_checkrequest(isr, &saidx); +} + /* * IPsec output logic for IPv6, transport mode. */ @@ -2714,10 +2890,8 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) { struct ip6_hdr *ip6; struct ipsecrequest *isr = NULL; - struct secasindex saidx; int error = 0; int plen; - struct sockaddr_in6 *sin6; if (!state) panic("state == NULL in ipsec6_output_trans"); @@ -2743,39 +2917,10 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) break; } - /* make SA index for search proper SA */ - ip6 = mtod(state->m, struct ip6_hdr *); - bcopy(&isr->saidx, &saidx, sizeof(saidx)); - saidx.mode = isr->saidx.mode; - saidx.reqid = isr->saidx.reqid; - sin6 = (struct sockaddr_in6 *)&saidx.src; - if (sin6->sin6_len == 0) { - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = IPSEC_PORT_ANY; - bcopy(&ip6->ip6_src, &sin6->sin6_addr, - sizeof(ip6->ip6_src)); - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { - /* fix scope id for comparing SPD */ - sin6->sin6_addr.s6_addr16[1] = 0; - sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); - } - } - sin6 = (struct sockaddr_in6 *)&saidx.dst; - if (sin6->sin6_len == 0) { - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = IPSEC_PORT_ANY; - bcopy(&ip6->ip6_dst, &sin6->sin6_addr, - sizeof(ip6->ip6_dst)); - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { - /* fix scope id for comparing SPD */ - sin6->sin6_addr.s6_addr16[1] = 0; - sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); - } - } - - if (key_checkrequest(isr, &saidx) == ENOENT) { + error = ipsec6_checksa(isr, state, 0); + if (error == EIO) + goto bad; + if (error == ENOENT) { /* * IPsec processing is required, but no SA found. * I assume that key_acquire() had been called @@ -2784,7 +2929,6 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) * upper layer to retransmit the packet. */ ipsec6stat.out_nosa++; - error = ENOENT; /* * Notify the fact that the packet is discarded @@ -2793,7 +2937,12 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) * XXX: should we restrict the error to TCP packets? * XXX: should we directly notify sockets via * pfctlinputs? + * + * Noone have initialized rcvif until this point, + * so clear it. */ + if ((state->m->m_flags & M_PKTHDR) != 0) + state->m->m_pkthdr.rcvif = NULL; icmp6_error(state->m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN, 0); state->m = NULL; /* icmp6_error freed the mbuf */ @@ -2802,7 +2951,7 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) /* validity check */ if (isr->sav == NULL) { - switch (ipsec_get_reqlevel(isr)) { + switch (ipsec_get_reqlevel(isr, AF_INET6)) { case IPSEC_LEVEL_USE: continue; case IPSEC_LEVEL_REQUIRE: @@ -2884,10 +3033,9 @@ ipsec6_output_tunnel(state, sp, flags) { struct ip6_hdr *ip6; struct ipsecrequest *isr = NULL; - struct secasindex saidx; int error = 0; int plen; - struct sockaddr_in6* dst6; + struct sockaddr_in6 *dst6; int s; if (!state) @@ -2911,48 +3059,10 @@ ipsec6_output_tunnel(state, sp, flags) } for (/* already initialized */; isr; isr = isr->next) { - if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { - /* When tunnel mode, SA peers must be specified. */ - bcopy(&isr->saidx, &saidx, sizeof(saidx)); - } else { - /* make SA index to look for a proper SA */ - struct sockaddr_in6 *sin6; - - bzero(&saidx, sizeof(saidx)); - saidx.proto = isr->saidx.proto; - saidx.mode = isr->saidx.mode; - saidx.reqid = isr->saidx.reqid; - - ip6 = mtod(state->m, struct ip6_hdr *); - sin6 = (struct sockaddr_in6 *)&saidx.src; - if (sin6->sin6_len == 0) { - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = IPSEC_PORT_ANY; - bcopy(&ip6->ip6_src, &sin6->sin6_addr, - sizeof(ip6->ip6_src)); - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { - /* fix scope id for comparing SPD */ - sin6->sin6_addr.s6_addr16[1] = 0; - sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); - } - } - sin6 = (struct sockaddr_in6 *)&saidx.dst; - if (sin6->sin6_len == 0) { - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = IPSEC_PORT_ANY; - bcopy(&ip6->ip6_dst, &sin6->sin6_addr, - sizeof(ip6->ip6_dst)); - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { - /* fix scope id for comparing SPD */ - sin6->sin6_addr.s6_addr16[1] = 0; - sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); - } - } - } - - if (key_checkrequest(isr, &saidx) == ENOENT) { + error = ipsec6_checksa(isr, state, 1); + if (error == EIO) + goto bad; + if (error == ENOENT) { /* * IPsec processing is required, but no SA found. * I assume that key_acquire() had been called @@ -2967,7 +3077,7 @@ ipsec6_output_tunnel(state, sp, flags) /* validity check */ if (isr->sav == NULL) { - switch (ipsec_get_reqlevel(isr)) { + switch (ipsec_get_reqlevel(isr, AF_INET6)) { case IPSEC_LEVEL_USE: continue; case IPSEC_LEVEL_REQUIRE: @@ -3027,7 +3137,7 @@ ipsec6_output_tunnel(state, sp, flags) state->dst = (struct sockaddr *)&state->ro->ro_dst; dst6 = (struct sockaddr_in6 *)state->dst; if (state->ro->ro_rt && - ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 || + (!(state->ro->ro_rt->rt_flags & RTF_UP) || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { RTFREE(state->ro->ro_rt); @@ -3219,11 +3329,7 @@ ipsec4_tunnel_validate(m, off, nxt0, sav) return 0; oip = mtod(m, struct ip *); -#ifdef _IP_VHL - hlen = _IP_VHL_HL(oip->ip_vhl) << 2; -#else hlen = oip->ip_hl << 2; -#endif if (hlen != sizeof(struct ip)) return 0; @@ -3273,6 +3379,14 @@ ipsec4_tunnel_validate(m, off, nxt0, sav) sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst, (struct sockaddr *)&isrc, (struct sockaddr *)&idst); + /* + * when there is no suitable inbound policy for the packet of the ipsec + * tunnel mode, the kernel never decapsulate the tunneled packet + * as the ipsec tunnel mode even when the system wide policy is "none". + * then the kernel leaves the generic tunnel module to process this + * packet. if there is no rule of the generic tunnel, the packet + * is rejected and the statistics will be counted up. + */ if (!sp) return 0; key_freesp(sp); @@ -3308,6 +3422,7 @@ ipsec6_tunnel_validate(m, off, nxt0, sav) return 0; oip6 = mtod(m, struct ip6_hdr *); + /* AF_INET should be supported, but at this moment we don't. */ sin6 = (struct sockaddr_in6 *)&sav->sah->saidx.dst; if (sin6->sin6_family != AF_INET6) @@ -3338,14 +3453,6 @@ ipsec6_tunnel_validate(m, off, nxt0, sav) sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst, (struct sockaddr *)&isrc, (struct sockaddr *)&idst); - /* - * when there is no suitable inbound policy for the packet of the ipsec - * tunnel mode, the kernel never decapsulate the tunneled packet - * as the ipsec tunnel mode even when the system wide policy is "none". - * then the kernel leaves the generic tunnel module to process this - * packet. if there is no rule of the generic tunnel, the packet - * is rejected and the statistics will be counted up. - */ if (!sp) return 0; key_freesp(sp); @@ -3374,8 +3481,7 @@ ipsec_copypkt(m) * references to the cluster. * XXX: is this approach effective? */ - if (n->m_ext.ext_type != EXT_CLUSTER || - MEXT_IS_REF(n)) { + if (!M_WRITABLE(n)) { int remain, copied; struct mbuf *mm; @@ -3383,10 +3489,16 @@ ipsec_copypkt(m) MGETHDR(mnew, M_DONTWAIT, MT_HEADER); if (mnew == NULL) goto fail; - if (!m_dup_pkthdr(mnew, n, M_DONTWAIT)) { - m_free(mnew); - goto fail; + mnew->m_pkthdr = n->m_pkthdr; +#if 0 + /* XXX: convert to m_tag or delete? */ + if (n->m_pkthdr.aux) { + mnew->m_pkthdr.aux = + m_copym(n->m_pkthdr.aux, + 0, M_COPYALL, M_DONTWAIT); } +#endif + M_MOVE_PKTHDR(mnew, n); } else { MGET(mnew, M_DONTWAIT, MT_DATA); @@ -3460,49 +3572,136 @@ ipsec_copypkt(m) return (NULL); } +static struct ipsecaux * +ipsec_addaux(m) + struct mbuf *m; +{ + struct m_tag *mtag; + + mtag = m_tag_find(m, PACKET_TAG_IPSEC_HISTORY, NULL); + if (mtag == NULL) { + mtag = m_tag_get(PACKET_TAG_IPSEC_HISTORY, + sizeof(struct ipsecaux), M_NOWAIT); + if (mtag != NULL) + m_tag_prepend(m, mtag); + } + if (mtag == NULL) + return NULL; /* ENOBUFS */ + /* XXX is this necessary? */ + bzero((void *)(mtag + 1), sizeof(struct ipsecaux)); + return mtag ? (struct ipsecaux *)(mtag + 1) : NULL; +} + +static struct ipsecaux * +ipsec_findaux(m) + struct mbuf *m; +{ + struct m_tag *mtag; + + mtag = m_tag_find(m, PACKET_TAG_IPSEC_HISTORY, NULL); + return mtag ? (struct ipsecaux *)(mtag + 1) : NULL; +} + void ipsec_delaux(m) struct mbuf *m; { struct m_tag *mtag; - while ((mtag = m_tag_find(m, PACKET_TAG_IPSEC_HISTORY, NULL)) != NULL) + mtag = m_tag_find(m, PACKET_TAG_IPSEC_HISTORY, NULL); + if (mtag != NULL) m_tag_delete(m, mtag); } +/* if the aux buffer is unnecessary, nuke it. */ +static void +ipsec_optaux(m, aux) + struct mbuf *m; + struct ipsecaux *aux; +{ + + if (aux == NULL) + return; + if (!aux->so && !aux->sp) + ipsec_delaux(m); +} + +int +ipsec_setsocket(m, so) + struct mbuf *m; + struct socket *so; +{ + struct ipsecaux *aux; + + /* if so == NULL, don't insist on getting the aux mbuf */ + if (so) { + aux = ipsec_addaux(m); + if (aux == NULL) + return ENOBUFS; + } else + aux = ipsec_findaux(m); + if (aux != NULL) { + aux->so = so; + } + ipsec_optaux(m, aux); + return 0; +} + +struct socket * +ipsec_getsocket(m) + struct mbuf *m; +{ + struct ipsecaux *aux; + + aux = ipsec_findaux(m); + if (aux != NULL) + return aux->so; + else + return NULL; +} + int ipsec_addhist(m, proto, spi) struct mbuf *m; int proto; u_int32_t spi; { - struct m_tag *mtag; - struct ipsec_history *p; + struct ipsecaux *aux; - mtag = m_tag_get(PACKET_TAG_IPSEC_HISTORY, - sizeof (struct ipsec_history), M_NOWAIT); - if (mtag == NULL) + aux = ipsec_addaux(m); + if (aux == NULL) return ENOBUFS; - p = (struct ipsec_history *)(mtag+1); - bzero(p, sizeof(*p)); - p->ih_proto = proto; - p->ih_spi = spi; - m_tag_prepend(m, mtag); + aux->hdrs++; return 0; } +int +ipsec_getnhist(m) + struct mbuf *m; +{ + struct ipsecaux *aux; + + aux = ipsec_findaux(m); + if (aux == NULL) + return 0; + return aux->hdrs; +} + struct ipsec_history * ipsec_gethist(m, lenp) struct mbuf *m; int *lenp; { - struct m_tag *mtag; - mtag = m_tag_find(m, PACKET_TAG_IPSEC_HISTORY, NULL); - if (mtag == NULL) - return NULL; - /* XXX NB: noone uses this so fake it */ - if (lenp) - *lenp = sizeof (struct ipsec_history); - return ((struct ipsec_history *)(mtag+1)); + panic("ipsec_gethist: obsolete API"); +} + +void +ipsec_clearhist(m) + struct mbuf *m; +{ + struct ipsecaux *aux; + + aux = ipsec_findaux(m); + ipsec_optaux(m, aux); } diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h index 7787fab..356c985 100644 --- a/sys/netinet6/ipsec.h +++ b/sys/netinet6/ipsec.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipsec.h,v 1.53 2001/11/20 08:32:38 itojun Exp $ */ +/* $KAME: ipsec.h,v 1.69 2003/09/10 23:49:11 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -54,7 +54,6 @@ * specifies ICMPv6 type, and the port field in "dst" specifies ICMPv6 code. */ struct secpolicyindex { - u_int8_t dir; /* direction of packet flow, see blow */ struct sockaddr_storage src; /* IP src address for SP */ struct sockaddr_storage dst; /* IP dst address for SP */ u_int8_t prefs; /* prefix length in bits for src */ @@ -70,16 +69,26 @@ struct secpolicyindex { /* Security Policy Data Base */ struct secpolicy { - LIST_ENTRY(secpolicy) chain; + TAILQ_ENTRY(secpolicy) tailq; /* all SPD entries, both pcb/table */ + LIST_ENTRY(secpolicy) chain; /* SPD entries on table */ + u_int8_t dir; /* direction of packet flow */ + int readonly; /* write prohibited */ + int persist; /* will never be removed */ int refcnt; /* reference count */ - struct secpolicyindex spidx; /* selector */ - u_int32_t id; /* It's unique number on the system. */ + struct secpolicyindex *spidx; /* selector - NULL if not valid */ + u_int32_t id; /* it identifies a policy in the SPD. */ +#define IPSEC_MANUAL_POLICYID_MAX 0x3fff + /* + * 1 - 0x3fff are reserved for user operation. + * 0 are reserved. Others are for kernel use. + */ + struct socket *so; /* backpointer to per-socket policy */ u_int state; /* 0: dead, others: alive */ #define IPSEC_SPSTATE_DEAD 0 #define IPSEC_SPSTATE_ALIVE 1 - u_int policy; /* DISCARD, NONE or IPSEC, see keyv2.h */ + int policy; /* DISCARD, NONE or IPSEC, see below */ struct ipsecrequest *req; /* pointer to the ipsec request tree, */ /* if policy == IPSEC else this value == NULL.*/ @@ -98,6 +107,7 @@ struct secpolicy { }; /* Request for IPsec */ +struct ifnet; struct ipsecrequest { struct ipsecrequest *next; /* pointer to next structure */ @@ -108,6 +118,8 @@ struct ipsecrequest { struct secasvar *sav; /* place holder of SA for use */ struct secpolicy *sp; /* back pointer to SP */ + + struct ifnet *tunifp; /* interface for tunnelling */ }; /* security policy in PCB */ @@ -115,6 +127,14 @@ struct inpcbpolicy { struct secpolicy *sp_in; struct secpolicy *sp_out; int priv; /* privileged socket ? */ + + /* cached policy */ + /* XXX 3 == IPSEC_DIR_MAX */ + struct secpolicy *cache[3]; + struct secpolicyindex cacheidx[3]; + int cachegen[3]; /* cache generation #, the time we filled it */ + int cacheflags; +#define IPSEC_PCBSP_CONNECTED 1 }; /* SP acquiring list table. */ @@ -127,6 +147,14 @@ struct secspacq { int count; /* for lifetime */ /* XXX: here is mbuf place holder to be sent ? */ }; + +struct ipsecaux { + struct socket *so; + int hdrs; /* # of ipsec headers */ + + struct secpolicy *sp; + struct ipsecrequest *req; +}; #endif /* _KERNEL */ /* according to IANA assignment, port 0x0000 and proto 0xff are reserved. */ @@ -210,6 +238,9 @@ struct ipsecstat { u_quad_t out_esphist[256]; u_quad_t out_ahhist[256]; u_quad_t out_comphist[256]; + + u_quad_t spdcachelookup; + u_quad_t spdcachemiss; }; /* @@ -274,6 +305,7 @@ struct ipsec_output_state { struct mbuf *m; struct route *ro; struct sockaddr *dst; + int encap; }; struct ipsec_history { @@ -283,8 +315,9 @@ struct ipsec_history { extern int ipsec_debug; +#ifdef INET extern struct ipsecstat ipsecstat; -extern struct secpolicy ip4_def_policy; +extern struct secpolicy *ip4_def_policy; extern int ip4_esp_trans_deflev; extern int ip4_esp_net_deflev; extern int ip4_ah_trans_deflev; @@ -294,21 +327,26 @@ extern int ip4_ah_offsetmask; extern int ip4_ipsec_dfbit; extern int ip4_ipsec_ecn; extern int ip4_esp_randpad; +#endif #define ipseclog(x) do { if (ipsec_debug) log x; } while (/*CONSTCOND*/ 0) -struct inpcb; -extern struct secpolicy *ipsec4_getpolicybypcb - __P((struct mbuf *, u_int, struct inpcb *, int *)); +extern int ipsec_pcbconn __P((struct inpcbpolicy *)); +extern int ipsec_pcbdisconn __P((struct inpcbpolicy *)); +extern int ipsec_invalpcbcacheall __P((void)); + extern struct secpolicy *ipsec4_getpolicybysock __P((struct mbuf *, u_int, struct socket *, int *)); extern struct secpolicy *ipsec4_getpolicybyaddr __P((struct mbuf *, u_int, int, int *)); +extern struct secpolicy *ipsec4_getpolicybytag + __P((struct mbuf *, u_int, int *)); -extern int ipsec_init_policy __P((struct socket *, struct inpcbpolicy **)); -extern int ipsec_copy_policy +struct inpcb; +extern int ipsec_init_pcbpolicy __P((struct socket *, struct inpcbpolicy **)); +extern int ipsec_copy_pcbpolicy __P((struct inpcbpolicy *, struct inpcbpolicy *)); -extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *)); +extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *, int)); extern int ipsec4_set_policy __P((struct inpcb *, int, caddr_t, size_t, int)); extern int ipsec4_get_policy __P((struct inpcb *, caddr_t, size_t, @@ -319,6 +357,7 @@ extern int ipsec4_in_reject __P((struct mbuf *, struct inpcb *)); struct secas; struct tcpcb; +struct tcp6cb; extern int ipsec_chkreplay __P((u_int32_t, struct secasvar *)); extern int ipsec_updatereplay __P((u_int32_t, struct secasvar *)); @@ -337,8 +376,13 @@ extern int ipsec4_tunnel_validate __P((struct mbuf *, int, u_int, struct secasvar *)); extern struct mbuf *ipsec_copypkt __P((struct mbuf *)); extern void ipsec_delaux __P((struct mbuf *)); +extern int ipsec_setsocket __P((struct mbuf *, struct socket *)); +extern struct socket *ipsec_getsocket __P((struct mbuf *)); extern int ipsec_addhist __P((struct mbuf *, int, u_int32_t)); +extern int ipsec_getnhist __P((struct mbuf *)); extern struct ipsec_history *ipsec_gethist __P((struct mbuf *, int *)); +extern void ipsec_clearhist __P((struct mbuf *)); + #endif /* _KERNEL */ #ifndef _KERNEL diff --git a/sys/netinet6/ipsec6.h b/sys/netinet6/ipsec6.h index 1811088..e99dea3 100644 --- a/sys/netinet6/ipsec6.h +++ b/sys/netinet6/ipsec6.h @@ -42,7 +42,7 @@ #ifdef _KERNEL extern struct ipsecstat ipsec6stat; -extern struct secpolicy ip6_def_policy; +extern struct secpolicy *ip6_def_policy; extern int ip6_esp_trans_deflev; extern int ip6_esp_net_deflev; extern int ip6_ah_trans_deflev; @@ -51,8 +51,6 @@ extern int ip6_ipsec_ecn; extern int ip6_esp_randpad; struct inpcb; -extern struct secpolicy *ipsec6_getpolicybypcb - __P((struct mbuf *, u_int, struct inpcb *, int *)); extern struct secpolicy *ipsec6_getpolicybysock __P((struct mbuf *, u_int, struct socket *, int *)); extern struct secpolicy *ipsec6_getpolicybyaddr @@ -60,10 +58,9 @@ extern struct secpolicy *ipsec6_getpolicybyaddr extern int ipsec6_in_reject_so __P((struct mbuf *, struct socket *)); extern int ipsec6_delete_pcbpolicy __P((struct inpcb *)); -extern int ipsec6_set_policy __P((struct inpcb *inp, int optname, - caddr_t request, size_t len, int priv)); -extern int ipsec6_get_policy - __P((struct inpcb *inp, caddr_t request, size_t len, struct mbuf **mp)); +extern int ipsec6_set_policy __P((struct inpcb *, int, caddr_t, size_t, int)); +extern int ipsec6_get_policy __P((struct inpcb *, caddr_t, size_t, + struct mbuf **)); extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *)); struct tcp6cb; diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 97b57e1..192b4ea 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1970,6 +1970,10 @@ nd6_output(ifp, origifp, m0, dst, rt0) return (0); sendpkt: +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); +#endif #ifdef MAC mac_create_mbuf_linklayer(ifp, m); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index 8a26928..fcda84f 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -487,6 +487,10 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) nd_ns->nd_ns_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); +#ifdef IPSEC + /* Don't lookup socket */ + (void)ipsec_setsocket(m, NULL); +#endif ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif, NULL); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); @@ -928,6 +932,10 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) nd_na->nd_na_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); +#ifdef IPSEC + /* Don't lookup socket */ + (void)ipsec_setsocket(m, NULL); +#endif ip6_output(m, NULL, NULL, 0, &im6o, &outif, NULL); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 7525338..b714a2b 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -180,7 +180,7 @@ rip6_input(mp, offp, proto) /* * Check AH/ESP integrity. */ - if (n && ipsec6_in_reject(n, last)) { + if (n && ipsec6_in_reject_so(n, last->inp_socket)) { m_freem(n); ipsec6stat.in_polvio++; /* do not inject data into pcb */ @@ -219,7 +219,7 @@ rip6_input(mp, offp, proto) /* * Check AH/ESP integrity. */ - if (last && ipsec6_in_reject(m, last)) { + if (last && ipsec6_in_reject_so(m, last->inp_socket)) { m_freem(m); ipsec6stat.in_polvio++; ip6stat.ip6s_delivered--; @@ -470,6 +470,13 @@ rip6_output(m, va_alist) *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); } +#ifdef IPSEC + if (ipsec_setsocket(m, so) != 0) { + error = ENOBUFS; + goto bad; + } +#endif /*IPSEC*/ + error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, 0, in6p->in6p_moptions, &oifp, in6p); if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c index f753a76..36a7fba 100644 --- a/sys/netinet6/udp6_output.c +++ b/sys/netinet6/udp6_output.c @@ -291,6 +291,12 @@ udp6_output(in6p, m, addr6, control, td) flags = 0; udp6stat.udp6s_opackets++; +#ifdef IPSEC + if (ipsec_setsocket(m, in6p->in6p_socket) != 0) { + error = ENOBUFS; + goto release; + } +#endif /* IPSEC */ error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, flags, in6p->in6p_moptions, NULL, in6p); break; diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index 93d7b1d..eb6bbac 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -261,7 +261,7 @@ udp6_input(mp, offp, proto) /* * Check AH/ESP integrity. */ - if (ipsec6_in_reject(m, last)) + if (ipsec6_in_reject_so(m, last->inp_socket)) ipsec6stat.in_polvio++; /* do not inject data into pcb */ else @@ -327,7 +327,7 @@ udp6_input(mp, offp, proto) /* * Check AH/ESP integrity. */ - if (ipsec6_in_reject(m, last)) { + if (ipsec6_in_reject_so(m, last->inp_socket)) { ipsec6stat.in_polvio++; goto bad; } @@ -383,7 +383,7 @@ udp6_input(mp, offp, proto) /* * Check AH/ESP integrity. */ - if (ipsec6_in_reject(m, in6p)) { + if (ipsec6_in_reject_so(m, in6p->inp_socket)) { ipsec6stat.in_polvio++; goto bad; } diff --git a/sys/netkey/key.c b/sys/netkey/key.c index c2cf71a..f24cff6 100644 --- a/sys/netkey/key.c +++ b/sys/netkey/key.c @@ -1,4 +1,4 @@ -/* $KAME: key.c,v 1.191 2001/06/27 10:46:49 sakane Exp $ */ +/* $KAME: key.c,v 1.308 2003/09/07 20:35:59 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -131,7 +131,6 @@ u_int32_t key_debug_level = 0; static u_int key_spi_trycnt = 1000; static u_int32_t key_spi_minval = 0x100; static u_int32_t key_spi_maxval = 0x0fffffff; /* XXX */ -static u_int32_t policy_id = 0; static u_int key_int_random = 60; /*interval to initialize randseed,1(m)*/ static u_int key_larval_lifetime = 30; /* interval to expire acquiring, 30(s)*/ static int key_blockacq_count = 10; /* counter for blocking SADB_ACQUIRE.*/ @@ -141,10 +140,17 @@ static int key_preferred_oldsa = 1; /* preferred old sa rather than new sa.*/ static u_int32_t acq_seq = 0; static int key_tick_init_random = 0; -static LIST_HEAD(_sptree, secpolicy) sptree[IPSEC_DIR_MAX]; /* SPD */ +struct _satailq satailq; /* list of all SAD entry */ +struct _sptailq sptailq; /* SPD table + pcb */ +static LIST_HEAD(_sptree, secpolicy) sptree[IPSEC_DIR_MAX]; /* SPD table */ static LIST_HEAD(_sahtree, secashead) sahtree; /* SAD */ static LIST_HEAD(_regtree, secreg) regtree[SADB_SATYPE_MAX + 1]; /* registed list */ + +#define SPIHASHSIZE 128 +#define SPIHASH(x) (((x) ^ ((x) >> 16)) % SPIHASHSIZE) +static LIST_HEAD(_spihash, secasvar) spihash[SPIHASHSIZE]; + #ifndef IPSEC_NONBLOCK_ACQUIRE static LIST_HEAD(_acqtree, secacq) acqtree; /* acquiring list */ #endif @@ -217,9 +223,7 @@ static int ipsec_esp_keymin = 256; static int ipsec_esp_auth = 0; static int ipsec_ah_keymin = 128; -#ifdef SYSCTL_DECL SYSCTL_DECL(_net_key); -#endif SYSCTL_INT(_net_key, KEYCTL_DEBUG_LEVEL, debug, CTLFLAG_RW, \ &key_debug_level, 0, ""); @@ -252,10 +256,6 @@ SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_COUNT, blockacq_count, CTLFLAG_RW, \ SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_LIFETIME, blockacq_lifetime, CTLFLAG_RW, \ &key_blockacq_lifetime, 0, ""); -/* ESP auth */ -SYSCTL_INT(_net_key, KEYCTL_ESP_AUTH, esp_auth, CTLFLAG_RW, \ - &ipsec_esp_auth, 0, ""); - /* minimum ESP key length */ SYSCTL_INT(_net_key, KEYCTL_ESP_KEYMIN, esp_keymin, CTLFLAG_RW, \ &ipsec_esp_keymin, 0, ""); @@ -268,10 +268,6 @@ SYSCTL_INT(_net_key, KEYCTL_AH_KEYMIN, ah_keymin, CTLFLAG_RW, \ SYSCTL_INT(_net_key, KEYCTL_PREFERED_OLDSA, preferred_oldsa, CTLFLAG_RW,\ &key_preferred_oldsa, 0, ""); -#ifndef LIST_FOREACH -#define LIST_FOREACH(elm, head, field) \ - for (elm = LIST_FIRST(head); elm; elm = LIST_NEXT(elm, field)) -#endif #define __LIST_CHAINED(elm) \ (!((elm)->chain.le_next == NULL && (elm)->chain.le_prev == NULL)) #define LIST_INSERT_TAIL(head, elm, type, field) \ @@ -308,7 +304,7 @@ do { \ #define KMALLOC(p, t, n) \ ((p) = (t) malloc((unsigned long)(n), M_SECA, M_NOWAIT)) #define KFREE(p) \ - free((caddr_t)(p), M_SECA); + free((caddr_t)(p), M_SECA) #else #define KMALLOC(p, t, n) \ do { \ @@ -328,15 +324,14 @@ do { \ * set parameters into secpolicyindex buffer. * Must allocate secpolicyindex buffer passed to this function. */ -#define KEY_SETSECSPIDX(_dir, s, d, ps, pd, ulp, idx) \ +#define KEY_SETSECSPIDX(s, d, ps, pd, ulp, idx) \ do { \ bzero((idx), sizeof(struct secpolicyindex)); \ - (idx)->dir = (_dir); \ (idx)->prefs = (ps); \ (idx)->prefd = (pd); \ (idx)->ul_proto = (ulp); \ - bcopy((s), &(idx)->src, ((const struct sockaddr *)(s))->sa_len); \ - bcopy((d), &(idx)->dst, ((const struct sockaddr *)(d))->sa_len); \ + bcopy((s), &(idx)->src, ((struct sockaddr *)(s))->sa_len); \ + bcopy((d), &(idx)->dst, ((struct sockaddr *)(d))->sa_len); \ } while (/*CONSTCOND*/ 0) /* @@ -349,8 +344,8 @@ do { \ (idx)->proto = (p); \ (idx)->mode = (m); \ (idx)->reqid = (r); \ - bcopy((s), &(idx)->src, ((const struct sockaddr *)(s))->sa_len); \ - bcopy((d), &(idx)->dst, ((const struct sockaddr *)(d))->sa_len); \ + bcopy((s), &(idx)->src, ((struct sockaddr *)(s))->sa_len); \ + bcopy((d), &(idx)->dst, ((struct sockaddr *)(d))->sa_len); \ } while (/*CONSTCOND*/ 0) /* key statistics */ @@ -366,18 +361,16 @@ struct sadb_msghdr { }; static struct secasvar *key_allocsa_policy(struct secasindex *); -static void key_freesp_so(struct secpolicy **); static struct secasvar *key_do_allocsa_policy(struct secashead *, u_int); static void key_delsav(struct secasvar *); static void key_delsp(struct secpolicy *); -static struct secpolicy *key_getsp(struct secpolicyindex *); +static struct secpolicy *key_getsp(struct secpolicyindex *, int); static struct secpolicy *key_getspbyid(u_int32_t); static u_int32_t key_newreqid(void); static struct mbuf *key_gather_mbuf(struct mbuf *, const struct sadb_msghdr *, int, int, ...); static int key_spdadd(struct socket *, struct mbuf *, const struct sadb_msghdr *); -static u_int32_t key_getnewspid(void); static int key_spddelete(struct socket *, struct mbuf *, const struct sadb_msghdr *); static int key_spddelete2(struct socket *, struct mbuf *, @@ -398,6 +391,7 @@ static struct secasvar *key_newsav(struct mbuf *, const struct sadb_msghdr *, struct secashead *, int *); static struct secashead *key_getsah(struct secasindex *); static struct secasvar *key_checkspidup(struct secasindex *, u_int32_t); +static void key_setspi(struct secasvar *, u_int32_t); static struct secasvar *key_getsavbyspi(struct secashead *, u_int32_t); static int key_setsaval(struct secasvar *, struct mbuf *, const struct sadb_msghdr *); @@ -414,9 +408,12 @@ static struct mbuf *key_setsadbident(u_int16_t, u_int16_t, caddr_t, int, u_int64_t); #endif static struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t, u_int32_t); +static struct mbuf *key_setsadblifetime(u_int16_t, u_int32_t, + u_int64_t, u_int64_t, u_int64_t); static struct mbuf *key_setsadbxpolicy(u_int16_t, u_int8_t, u_int32_t); static void *key_newbuf(const void *, u_int); +static int key_ismyaddr(struct sockaddr *); #ifdef INET6 static int key_ismyaddr6(struct sockaddr_in6 *); #endif @@ -424,18 +421,14 @@ static int key_ismyaddr6(struct sockaddr_in6 *); /* flags for key_cmpsaidx() */ #define CMP_HEAD 1 /* protocol, addresses. */ #define CMP_MODE_REQID 2 /* additionally HEAD, reqid, mode. */ -#define CMP_REQID 3 /* additionally HEAD, reaid. */ +#define CMP_REQID 3 /* additionally HEAD, reqid. not used */ #define CMP_EXACTLY 4 /* all elements. */ -static int key_cmpsaidx - (struct secasindex *, struct secasindex *, int); +static int key_cmpsaidx(struct secasindex *, struct secasindex *, int); -static int key_cmpspidx_exactly - (struct secpolicyindex *, struct secpolicyindex *); -static int key_cmpspidx_withmask - (struct secpolicyindex *, struct secpolicyindex *); static int key_sockaddrcmp(struct sockaddr *, struct sockaddr *, int); static int key_bbcmp(caddr_t, caddr_t, u_int); static void key_srandom(void); +static u_long key_random(void); static u_int16_t key_satype2proto(u_int8_t); static u_int8_t key_proto2satype(u_int16_t); @@ -487,13 +480,15 @@ static int key_dump(struct socket *, struct mbuf *, static int key_promisc(struct socket *, struct mbuf *, const struct sadb_msghdr *); static int key_senderror(struct socket *, struct mbuf *, int); -static int key_validate_ext(const struct sadb_ext *, int); +static int key_validate_ext(struct sadb_ext *, int); static int key_align(struct mbuf *, struct sadb_msghdr *); #if 0 static const char *key_getfqdn(void); static const char *key_getuserfqdn(void); #endif static void key_sa_chgstate(struct secasvar *, u_int8_t); +static void key_sp_dead(struct secpolicy *); +static void key_sp_unlink(struct secpolicy *); static struct mbuf *key_alloc_mbuf(int); /* %%% IPsec policy management */ @@ -504,18 +499,14 @@ static struct mbuf *key_alloc_mbuf(int); * others: found and return the pointer. */ struct secpolicy * -key_allocsp(spidx, dir) +key_allocsp(tag, spidx, dir) + u_int16_t tag; struct secpolicyindex *spidx; u_int dir; { struct secpolicy *sp; - struct timeval tv; int s; - /* sanity check */ - if (spidx == NULL) - panic("key_allocsp: NULL pointer is passed."); - /* check direction */ switch (dir) { case IPSEC_DIR_INBOUND: @@ -527,19 +518,26 @@ key_allocsp(spidx, dir) /* get a SP entry */ s = splnet(); /*called from softclock()*/ - KEYDEBUG(KEYDEBUG_IPSEC_DATA, - printf("*** objects\n"); - kdebug_secpolicyindex(spidx)); - - LIST_FOREACH(sp, &sptree[dir], chain) { + if (spidx) { KEYDEBUG(KEYDEBUG_IPSEC_DATA, - printf("*** in SPD\n"); - kdebug_secpolicyindex(&sp->spidx)); + printf("*** objects\n"); + kdebug_secpolicyindex(spidx)); + } + LIST_FOREACH(sp, &sptree[dir], chain) { if (sp->state == IPSEC_SPSTATE_DEAD) continue; - if (key_cmpspidx_withmask(&sp->spidx, spidx)) - goto found; + if (sp->spidx) { + if (!spidx) + continue; + + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("*** in SPD\n"); + kdebug_secpolicyindex(sp->spidx)); + + if (key_cmpspidx_withmask(sp->spidx, spidx)) + goto found; + } } splx(s); @@ -547,11 +545,10 @@ key_allocsp(spidx, dir) found: /* sanity check */ - KEY_CHKSPDIR(sp->spidx.dir, dir, "key_allocsp"); + KEY_CHKSPDIR(sp->dir, dir, "key_allocsp"); /* found a SPD entry */ - microtime(&tv); - sp->lastused = tv.tv_sec; + sp->lastused = time_second; sp->refcnt++; splx(s); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, @@ -571,7 +568,6 @@ key_gettunnel(osrc, odst, isrc, idst) { struct secpolicy *sp; const int dir = IPSEC_DIR_INBOUND; - struct timeval tv; int s; struct ipsecrequest *r1, *r2, *p; struct sockaddr *os, *od, *is, *id; @@ -597,15 +593,22 @@ key_gettunnel(osrc, odst, isrc, idst) r2 = p; if (!r1) { - /* here we look at address matches only */ - spidx = sp->spidx; - if (isrc->sa_len > sizeof(spidx.src) || - idst->sa_len > sizeof(spidx.dst)) - continue; - bcopy(isrc, &spidx.src, isrc->sa_len); - bcopy(idst, &spidx.dst, idst->sa_len); - if (!key_cmpspidx_withmask(&sp->spidx, &spidx)) - continue; + if (sp->spidx) { + /* + * here we look at address matches + * only + */ + spidx = *sp->spidx; + if (isrc->sa_len > sizeof(spidx.src) || + idst->sa_len > sizeof(spidx.dst)) + continue; + bcopy(isrc, &spidx.src, isrc->sa_len); + bcopy(idst, &spidx.dst, idst->sa_len); + if (!key_cmpspidx_withmask(sp->spidx, + &spidx)) + continue; + } else + ; /* can't check for tagged policy */ } else { is = (struct sockaddr *)&r1->saidx.src; id = (struct sockaddr *)&r1->saidx.dst; @@ -627,8 +630,7 @@ key_gettunnel(osrc, odst, isrc, idst) return NULL; found: - microtime(&tv); - sp->lastused = tv.tv_sec; + sp->lastused = time_second; sp->refcnt++; splx(s); return sp; @@ -663,7 +665,7 @@ key_checkrequest(isr, saidx) } /* get current level */ - level = ipsec_get_reqlevel(isr); + level = ipsec_get_reqlevel(isr, saidx->src.ss_family); #if 0 /* @@ -673,8 +675,8 @@ key_checkrequest(isr, saidx) if (isr->sav != NULL) { if (isr->sav->sah == NULL) panic("key_checkrequest: sah is null."); - if (isr->sav == (struct secasvar *)LIST_FIRST( - &isr->sav->sah->savtree[SADB_SASTATE_DEAD])) { + if (isr->sav == + LIST_FIRST(&isr->sav->sah->savtree[SADB_SASTATE_DEAD])) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP checkrequest calls free SA:%p\n", isr->sav)); @@ -837,12 +839,13 @@ key_do_allocsa_policy(sah, state) * permanent. */ if (d->lft_c->sadb_lifetime_addtime != 0) { - struct mbuf *m, *result; + struct mbuf *m, *result = NULL; key_sa_chgstate(d, SADB_SASTATE_DEAD); m = key_setsadbmsg(SADB_DELETE, 0, - d->sah->saidx.proto, 0, 0, d->refcnt - 1); + key_proto2satype(d->sah->saidx.proto), + 0, 0, d->refcnt - 1); if (!m) goto msgfail; result = m; @@ -850,17 +853,15 @@ key_do_allocsa_policy(sah, state) /* set sadb_address for saidx's. */ m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, (struct sockaddr *)&d->sah->saidx.src, - d->sah->saidx.src.ss_len << 3, - IPSEC_ULPROTO_ANY); + FULLMASK, IPSEC_ULPROTO_ANY); if (!m) goto msgfail; m_cat(result, m); /* set sadb_address for saidx's. */ m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, - (struct sockaddr *)&d->sah->saidx.src, - d->sah->saidx.src.ss_len << 3, - IPSEC_ULPROTO_ANY); + (struct sockaddr *)&d->sah->saidx.dst, + FULLMASK, IPSEC_ULPROTO_ANY); if (!m) goto msgfail; m_cat(result, m); @@ -887,7 +888,12 @@ key_do_allocsa_policy(sah, state) if (key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED)) goto msgfail; + + result = NULL; + msgfail: + if (result != NULL) + m_freem(result); key_freesav(d); } } @@ -923,9 +929,8 @@ key_allocsa(family, src, dst, proto, spi) caddr_t src, dst; u_int32_t spi; { - struct secashead *sah; - struct secasvar *sav; - u_int stateidx, state; + struct secasvar *sav, *match; + u_int stateidx, state, tmpidx, matchidx; struct sockaddr_in sin; struct sockaddr_in6 sin6; int s; @@ -955,114 +960,120 @@ key_allocsa(family, src, dst, proto, spi) * encrypted so we can't check internal IP header. */ s = splnet(); /*called from softclock()*/ - LIST_FOREACH(sah, &sahtree, chain) { - /* - * search a valid state list for inbound packet. - * the search order is not important. - */ - for (stateidx = 0; stateidx < arraysize; stateidx++) { + /* + * search a valid state list for inbound packet. + * the search order is not important. + */ + match = NULL; + matchidx = arraysize; + LIST_FOREACH(sav, &spihash[SPIHASH(spi)], spihash) { + if (sav->spi != spi) + continue; + if (proto != sav->sah->saidx.proto) + continue; + if (family != sav->sah->saidx.src.ss_family || + family != sav->sah->saidx.dst.ss_family) + continue; + tmpidx = arraysize; + for (stateidx = 0; stateidx < matchidx; stateidx++) { state = saorder_state_valid[stateidx]; - LIST_FOREACH(sav, &sah->savtree[state], chain) { - /* sanity check */ - KEY_CHKSASTATE(sav->state, state, "key_allocsav"); - if (proto != sav->sah->saidx.proto) - continue; - if (spi != sav->spi) - continue; - if (family != sav->sah->saidx.src.ss_family || - family != sav->sah->saidx.dst.ss_family) - continue; + if (sav->state == state) { + tmpidx = stateidx; + break; + } + } + if (tmpidx >= matchidx) + continue; #if 0 /* don't check src */ - /* check src address */ - switch (family) { - case AF_INET: - bzero(&sin, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_len = sizeof(sin); - bcopy(src, &sin.sin_addr, - sizeof(sin.sin_addr)); - if (key_sockaddrcmp((struct sockaddr*)&sin, - (struct sockaddr *)&sav->sah->saidx.src, 0) != 0) - continue; + /* check src address */ + switch (family) { + case AF_INET: + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + bcopy(src, &sin.sin_addr, + sizeof(sin.sin_addr)); + if (key_sockaddrcmp((struct sockaddr*)&sin, + (struct sockaddr *)&sav->sah->saidx.src, 0) != 0) + continue; - break; - case AF_INET6: - bzero(&sin6, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_len = sizeof(sin6); - bcopy(src, &sin6.sin6_addr, - sizeof(sin6.sin6_addr)); - if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { - /* kame fake scopeid */ - sin6.sin6_scope_id = - ntohs(sin6.sin6_addr.s6_addr16[1]); - sin6.sin6_addr.s6_addr16[1] = 0; - } - if (key_sockaddrcmp((struct sockaddr*)&sin6, - (struct sockaddr *)&sav->sah->saidx.src, 0) != 0) - continue; - break; - default: - ipseclog((LOG_DEBUG, "key_allocsa: " - "unknown address family=%d.\n", - family)); - continue; - } + break; + case AF_INET6: + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(sin6); + bcopy(src, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); + if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { + /* kame fake scopeid */ + sin6.sin6_scope_id = + ntohs(sin6.sin6_addr.s6_addr16[1]); + sin6.sin6_addr.s6_addr16[1] = 0; + } + if (key_sockaddrcmp((struct sockaddr *)&sin6, + (struct sockaddr *)&sav->sah->saidx.src, 0) != 0) + continue; + break; + default: + ipseclog((LOG_DEBUG, "key_allocsa: " + "unknown address family=%d.\n", + family)); + continue; + } #endif - /* check dst address */ - switch (family) { - case AF_INET: - bzero(&sin, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_len = sizeof(sin); - bcopy(dst, &sin.sin_addr, - sizeof(sin.sin_addr)); - if (key_sockaddrcmp((struct sockaddr*)&sin, - (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) - continue; - - break; - case AF_INET6: - bzero(&sin6, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_len = sizeof(sin6); - bcopy(dst, &sin6.sin6_addr, - sizeof(sin6.sin6_addr)); - if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { - /* kame fake scopeid */ - sin6.sin6_scope_id = - ntohs(sin6.sin6_addr.s6_addr16[1]); - sin6.sin6_addr.s6_addr16[1] = 0; - } - if (key_sockaddrcmp((struct sockaddr*)&sin6, - (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) - continue; - break; - default: - ipseclog((LOG_DEBUG, "key_allocsa: " - "unknown address family=%d.\n", - family)); - continue; - } + /* check dst address */ + switch (family) { + case AF_INET: + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + bcopy(dst, &sin.sin_addr, + sizeof(sin.sin_addr)); + if (key_sockaddrcmp((struct sockaddr*)&sin, + (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) + continue; - goto found; + break; + case AF_INET6: + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(sin6); + bcopy(dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); + if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { + /* kame fake scopeid */ + sin6.sin6_scope_id = + ntohs(sin6.sin6_addr.s6_addr16[1]); + sin6.sin6_addr.s6_addr16[1] = 0; } + if (key_sockaddrcmp((struct sockaddr *)&sin6, + (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) + continue; + break; + default: + ipseclog((LOG_DEBUG, "key_allocsa: " + "unknown address family=%d.\n", family)); + continue; } + + match = sav; + matchidx = tmpidx; } + if (match) + goto found; + /* not found */ splx(s); return NULL; found: - sav->refcnt++; + match->refcnt++; splx(s); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP allocsa cause refcnt++:%d SA:%p\n", - sav->refcnt, sav)); - return sav; + match->refcnt, match)); + return match; } /* @@ -1089,79 +1100,6 @@ key_freesp(sp) } /* - * Must be called after calling key_allocsp(). - * For the packet with socket. - */ -void -key_freeso(so) - struct socket *so; -{ - /* sanity check */ - if (so == NULL) - panic("key_freeso: NULL pointer is passed."); - - switch (so->so_proto->pr_domain->dom_family) { -#ifdef INET - case PF_INET: - { - struct inpcb *pcb = sotoinpcb(so); - - /* Does it have a PCB ? */ - if (pcb == NULL) - return; - key_freesp_so(&pcb->inp_sp->sp_in); - key_freesp_so(&pcb->inp_sp->sp_out); - } - break; -#endif -#ifdef INET6 - case PF_INET6: - { - struct in6pcb *pcb = sotoin6pcb(so); - - /* Does it have a PCB ? */ - if (pcb == NULL) - return; - key_freesp_so(&pcb->in6p_sp->sp_in); - key_freesp_so(&pcb->in6p_sp->sp_out); - } - break; -#endif /* INET6 */ - default: - ipseclog((LOG_DEBUG, "key_freeso: unknown address family=%d.\n", - so->so_proto->pr_domain->dom_family)); - return; - } - - return; -} - -static void -key_freesp_so(sp) - struct secpolicy **sp; -{ - /* sanity check */ - if (sp == NULL || *sp == NULL) - panic("key_freesp_so: sp == NULL"); - - switch ((*sp)->policy) { - case IPSEC_POLICY_IPSEC: - KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP freeso calls free SP:%p\n", *sp)); - key_freesp(*sp); - *sp = NULL; - break; - case IPSEC_POLICY_ENTRUST: - case IPSEC_POLICY_BYPASS: - return; - default: - panic("key_freesp_so: Invalid policy found %d", (*sp)->policy); - } - - return; -} - -/* * Must be called after calling key_allocsa(). * This function is called by key_freesp() to free some SA allocated * for a policy. @@ -1185,13 +1123,12 @@ key_freesav(sav) key_delsav(sav); } -/* - * free() SA variable entry. - */ static void key_delsav(sav) struct secasvar *sav; { + int s; + /* sanity check */ if (sav == NULL) panic("key_delsav: NULL pointer is passed."); @@ -1199,10 +1136,14 @@ key_delsav(sav) if (sav->refcnt > 0) panic("key_delsav: called with positive refcnt"); - /* remove from SA header */ + s = splnet(); + if (__LIST_CHAINED(sav)) LIST_REMOVE(sav, chain); + if (sav->spihash.le_prev || sav->spihash.le_next) + LIST_REMOVE(sav, spihash); + if (sav->key_auth != NULL) { bzero(_KEYBUF(sav->key_auth), _KEYLEN(sav->key_auth)); KFREE(sav->key_auth); @@ -1241,7 +1182,7 @@ key_delsav(sav) keydb_delsecasvar(sav); - return; + splx(s); } /* %%% SPD management */ @@ -1258,15 +1199,10 @@ key_delsp(sp) if (sp == NULL) panic("key_delsp: NULL pointer is passed."); - sp->state = IPSEC_SPSTATE_DEAD; - if (sp->refcnt > 0) panic("key_delsp: called with positive refcnt"); s = splnet(); /*called from softclock()*/ - /* remove from SP index */ - if (__LIST_CHAINED(sp)) - LIST_REMOVE(sp, chain); { struct ipsecrequest *isr = sp->req, *nextisr; @@ -1299,8 +1235,9 @@ key_delsp(sp) * others : found, pointer to a SP. */ static struct secpolicy * -key_getsp(spidx) +key_getsp(spidx, dir) struct secpolicyindex *spidx; + int dir; { struct secpolicy *sp; @@ -1308,10 +1245,12 @@ key_getsp(spidx) if (spidx == NULL) panic("key_getsp: NULL pointer is passed."); - LIST_FOREACH(sp, &sptree[spidx->dir], chain) { + LIST_FOREACH(sp, &sptree[dir], chain) { if (sp->state == IPSEC_SPSTATE_DEAD) continue; - if (key_cmpspidx_exactly(spidx, &sp->spidx)) { + if (!sp->spidx) + continue; + if (key_cmpspidx_exactly(spidx, sp->spidx)) { sp->refcnt++; return sp; } @@ -1331,18 +1270,7 @@ key_getspbyid(id) { struct secpolicy *sp; - LIST_FOREACH(sp, &sptree[IPSEC_DIR_INBOUND], chain) { - if (sp->state == IPSEC_SPSTATE_DEAD) - continue; - if (sp->id == id) { - sp->refcnt++; - return sp; - } - } - - LIST_FOREACH(sp, &sptree[IPSEC_DIR_OUTBOUND], chain) { - if (sp->state == IPSEC_SPSTATE_DEAD) - continue; + TAILQ_FOREACH(sp, &sptailq, tailq) { if (sp->id == id) { sp->refcnt++; return sp; @@ -1353,14 +1281,41 @@ key_getspbyid(id) } struct secpolicy * -key_newsp() +key_newsp(id) + u_int32_t id; { - struct secpolicy *newsp = NULL; + struct secpolicy *newsp = NULL, *sp; + u_int32_t newid; + + if (id > IPSEC_MANUAL_POLICYID_MAX) { + ipseclog((LOG_DEBUG, + "key_newsp: policy_id=%u range " + "violation, updated by kernel.\n", id)); + id = 0; + } + + if (id == 0) { + if ((newid = keydb_newspid()) == 0) { + ipseclog((LOG_DEBUG, + "key_newsp: new policy_id allocation failed.")); + return NULL; + } + } else { + sp = key_getspbyid(id); + if (sp != NULL) { + ipseclog((LOG_DEBUG, + "key_newsp: policy_id(%u) has been used.\n", id)); + key_freesp(sp); + return NULL; + } + newid = id; + } newsp = keydb_newsecpolicy(); if (!newsp) return newsp; + newsp->id = newid; newsp->refcnt = 1; newsp->req = NULL; @@ -1391,12 +1346,12 @@ key_msg2sp(xpl0, len, error) return NULL; } - if ((newsp = key_newsp()) == NULL) { + if ((newsp = key_newsp(xpl0->sadb_x_policy_id)) == NULL) { *error = ENOBUFS; return NULL; } - newsp->spidx.dir = xpl0->sadb_x_policy_dir; + newsp->dir = xpl0->sadb_x_policy_dir; newsp->policy = xpl0->sadb_x_policy_type; /* check policy */ @@ -1638,7 +1593,7 @@ key_sp2msg(sp) xpl->sadb_x_policy_len = PFKEY_UNIT64(tlen); xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY; xpl->sadb_x_policy_type = sp->policy; - xpl->sadb_x_policy_dir = sp->spidx.dir; + xpl->sadb_x_policy_dir = sp->dir; xpl->sadb_x_policy_id = sp->id; p = (caddr_t)xpl + sizeof(*xpl); @@ -1753,6 +1708,7 @@ key_gather_mbuf(m, mhp, ndeep, nitem, va_alist) return result; fail: + va_end(ap); m_freem(result); return NULL; } @@ -1779,26 +1735,34 @@ key_spdadd(so, m, mhp) struct mbuf *m; const struct sadb_msghdr *mhp; { - struct sadb_address *src0, *dst0; + struct sadb_address *src0 = NULL, *dst0 = NULL; struct sadb_x_policy *xpl0, *xpl; struct sadb_lifetime *lft = NULL; struct secpolicyindex spidx; struct secpolicy *newsp; - struct timeval tv; + struct ipsecrequest *isr; int error; + int spidxmode; /* sanity check */ if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_spdadd: NULL pointer is passed."); - if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || - mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || - mhp->ext[SADB_X_EXT_POLICY] == NULL) { + if (mhp->ext[SADB_EXT_ADDRESS_SRC] != NULL && + mhp->ext[SADB_EXT_ADDRESS_DST] != NULL) { + ; + } else { ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n")); return key_senderror(so, m, EINVAL); } - if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || - mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) || + if (mhp->ext[SADB_X_EXT_POLICY] == NULL) { + ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n")); + return key_senderror(so, m, EINVAL); + } + if ((mhp->extlen[SADB_EXT_ADDRESS_SRC] && + mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address)) || + (mhp->extlen[SADB_EXT_ADDRESS_DST] && + mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) || mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n")); return key_senderror(so, m, EINVAL); @@ -1812,19 +1776,19 @@ key_spdadd(so, m, mhp) 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]; - xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; + /* spidx mode, or tag mode */ + spidxmode = (mhp->ext[SADB_EXT_ADDRESS_SRC] != NULL); - /* make secindex */ - /* XXX boundary check against sa_len */ - KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, - src0 + 1, - dst0 + 1, - src0->sadb_address_prefixlen, - dst0->sadb_address_prefixlen, - src0->sadb_address_proto, - &spidx); + if (spidxmode) { + src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; + dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; + /* make secindex */ + /* XXX boundary check against sa_len */ + KEY_SETSECSPIDX(src0 + 1, dst0 + 1, + src0->sadb_address_prefixlen, dst0->sadb_address_prefixlen, + src0->sadb_address_proto, &spidx); + } + xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; /* checking the direciton. */ switch (xpl0->sadb_x_policy_dir) { @@ -1859,11 +1823,25 @@ key_spdadd(so, m, mhp) * If the type is either SPDADD or SPDSETIDX AND a SP is found, * then error. */ - newsp = key_getsp(&spidx); + if (xpl0->sadb_x_policy_id != 0) + newsp = key_getspbyid(xpl0->sadb_x_policy_id); + else if (spidxmode) + newsp = key_getsp(&spidx, xpl0->sadb_x_policy_dir); + else + newsp = NULL; + + if (newsp && (newsp->readonly || newsp->persist)) { + ipseclog((LOG_DEBUG, + "key_spdadd: tried to alter readonly/persistent SP.\n")); + return key_senderror(so, m, EPERM); + } + 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) { @@ -1878,71 +1856,117 @@ key_spdadd(so, m, mhp) return key_senderror(so, m, error); } - if ((newsp->id = key_getnewspid()) == 0) { - keydb_delsecpolicy(newsp); - return key_senderror(so, m, ENOBUFS); - } - - /* XXX boundary check against sa_len */ - KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, - src0 + 1, - dst0 + 1, - src0->sadb_address_prefixlen, - dst0->sadb_address_prefixlen, - src0->sadb_address_proto, - &newsp->spidx); + if (spidxmode) { + error = keydb_setsecpolicyindex(newsp, &spidx); + if (error) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, error); + } - /* sanity check on addr pair */ - if (((struct sockaddr *)(src0 + 1))->sa_family != - ((struct sockaddr *)(dst0+ 1))->sa_family) { - keydb_delsecpolicy(newsp); - return key_senderror(so, m, EINVAL); - } - if (((struct sockaddr *)(src0 + 1))->sa_len != - ((struct sockaddr *)(dst0+ 1))->sa_len) { - keydb_delsecpolicy(newsp); - return key_senderror(so, m, EINVAL); - } -#if 1 - if (newsp->req && newsp->req->saidx.src.ss_family) { - struct sockaddr *sa; - sa = (struct sockaddr *)(src0 + 1); - if (sa->sa_family != newsp->req->saidx.src.ss_family) { + /* sanity check on addr pair */ + if (((struct sockaddr *)(src0 + 1))->sa_family != + ((struct sockaddr *)(dst0 + 1))->sa_family) { keydb_delsecpolicy(newsp); return key_senderror(so, m, EINVAL); } - } - if (newsp->req && newsp->req->saidx.dst.ss_family) { - struct sockaddr *sa; - sa = (struct sockaddr *)(dst0 + 1); - if (sa->sa_family != newsp->req->saidx.dst.ss_family) { + if (((struct sockaddr *)(src0 + 1))->sa_len != + ((struct sockaddr *)(dst0 + 1))->sa_len) { keydb_delsecpolicy(newsp); return key_senderror(so, m, EINVAL); } } -#endif - microtime(&tv); - newsp->created = tv.tv_sec; - newsp->lastused = tv.tv_sec; + for (isr = newsp->req; isr; isr = isr->next) { + struct sockaddr *sa; + + /* + * port spec is not permitted for tunnel mode + */ + if (isr->saidx.mode == IPSEC_MODE_TUNNEL && src0 && dst0) { + sa = (struct sockaddr *)(src0 + 1); + switch (sa->sa_family) { + case AF_INET: + if (((struct sockaddr_in *)sa)->sin_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + case AF_INET6: + if (((struct sockaddr_in6 *)sa)->sin6_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + default: + break; + } + sa = (struct sockaddr *)(dst0 + 1); + switch (sa->sa_family) { + case AF_INET: + if (((struct sockaddr_in *)sa)->sin_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + case AF_INET6: + if (((struct sockaddr_in6 *)sa)->sin6_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + default: + break; + } + } + } + + /* + * bark if we have different address family on tunnel address + * specification. applies only if we decapsulate in RFC2401 + * IPsec (implementation limitation). + */ + for (isr = newsp->req; isr; isr = isr->next) { + struct sockaddr *sa; + + if (isr->saidx.src.ss_family && src0) { + sa = (struct sockaddr *)(src0 + 1); + if (sa->sa_family != isr->saidx.src.ss_family) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + } + if (isr->saidx.dst.ss_family && dst0) { + sa = (struct sockaddr *)(dst0 + 1); + if (sa->sa_family != isr->saidx.dst.ss_family) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + } + } + + newsp->created = time_second; + newsp->lastused = time_second; 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); + LIST_INSERT_TAIL(&sptree[newsp->dir], newsp, secpolicy, chain); /* delete the entry in spacqtree */ - if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { + if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE && + mhp->ext[SADB_EXT_ADDRESS_SRC]) { struct secspacq *spacq; if ((spacq = key_getspacq(&spidx)) != NULL) { /* reset counter in order to deletion by timehandler. */ - microtime(&tv); - spacq->created = tv.tv_sec; + spacq->created = time_second; spacq->count = 0; } } + /* invalidate all cached SPD pointers on pcb */ + ipsec_invalpcbcacheall(); + { struct mbuf *n, *mpolicy; struct sadb_msg *newmsg; @@ -1990,37 +2014,6 @@ key_spdadd(so, m, mhp) } /* - * get new policy id. - * OUT: - * 0: failure. - * others: success. - */ -static u_int32_t -key_getnewspid() -{ - u_int32_t newid = 0; - int count = key_spi_trycnt; /* XXX */ - struct secpolicy *sp; - - /* when requesting to allocate spi ranged */ - while (count--) { - newid = (policy_id = (policy_id == ~0 ? 1 : policy_id + 1)); - - if ((sp = key_getspbyid(newid)) == NULL) - break; - - key_freesp(sp); - } - - if (count == 0 || newid == 0) { - ipseclog((LOG_DEBUG, "key_getnewspid: to allocate policy id is failed.\n")); - return 0; - } - - return newid; -} - -/* * SADB_SPDDELETE processing * receive * @@ -2066,8 +2059,7 @@ key_spddelete(so, m, mhp) /* make secindex */ /* XXX boundary check against sa_len */ - KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, - src0 + 1, + KEY_SETSECSPIDX(src0 + 1, dst0 + 1, src0->sadb_address_prefixlen, dst0->sadb_address_prefixlen, @@ -2085,16 +2077,28 @@ key_spddelete(so, m, mhp) } /* Is there SP in SPD ? */ - if ((sp = key_getsp(&spidx)) == NULL) { + if ((sp = key_getsp(&spidx, xpl0->sadb_x_policy_dir)) == NULL) { ipseclog((LOG_DEBUG, "key_spddelete: no SP found.\n")); return key_senderror(so, m, EINVAL); } + if (sp->persist) { + ipseclog((LOG_DEBUG, + "key_spddelete2: attempt to remove persistent SP:%u.\n", + sp->id)); + return key_senderror(so, m, EPERM); + } + /* save policy id 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(); { struct mbuf *n; @@ -2123,7 +2127,7 @@ key_spddelete(so, m, mhp) * and send, * * to the ikmpd. - * policy(*) including direction of policy. + * policy(*) including the policy id. * * m will always be freed. */ @@ -2143,8 +2147,7 @@ key_spddelete2(so, m, mhp) if (mhp->ext[SADB_X_EXT_POLICY] == NULL || mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { ipseclog((LOG_DEBUG, "key_spddelete2: invalid message is passed.\n")); - key_senderror(so, m, EINVAL); - return 0; + return key_senderror(so, m, EINVAL); } id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; @@ -2153,11 +2156,23 @@ key_spddelete2(so, m, mhp) if ((sp = key_getspbyid(id)) == NULL) { ipseclog((LOG_DEBUG, "key_spddelete2: no SP found id:%u.\n", id)); - key_senderror(so, m, EINVAL); + return key_senderror(so, m, EINVAL); } - sp->state = IPSEC_SPSTATE_DEAD; - key_freesp(sp); + if (sp->persist) { + ipseclog((LOG_DEBUG, + "key_spddelete2: attempt to remove persistent SP:%u.\n", + id)); + return key_senderror(so, m, EPERM); + } + + 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(); { struct mbuf *n, *nn; @@ -2213,7 +2228,7 @@ key_spddelete2(so, m, mhp) } /* - * SADB_X_GET processing + * SADB_X_SPDGET processing * receive * * from the user(?), @@ -2280,8 +2295,10 @@ key_spdacquire(sp) struct secpolicy *sp; { struct mbuf *result = NULL, *m; +#ifndef IPSEC_NONBLOCK_ACQUIRE struct secspacq *newspacq; - int error; +#endif + int error = -1; /* sanity check */ if (sp == NULL) @@ -2290,9 +2307,14 @@ key_spdacquire(sp) panic("key_spdacquire: called but there is request."); if (sp->policy != IPSEC_POLICY_IPSEC) panic("key_spdacquire: policy mismathed. IPsec is expected."); + if (!sp->spidx) { + error = EOPNOTSUPP; + goto fail; + } +#ifndef IPSEC_NONBLOCK_ACQUIRE /* get an entry to check whether sent message or not. */ - if ((newspacq = key_getspacq(&sp->spidx)) != NULL) { + if ((newspacq = key_getspacq(sp->spidx)) != NULL) { if (key_blockacq_count < newspacq->count) { /* reset counter and do send message. */ newspacq->count = 0; @@ -2303,12 +2325,13 @@ key_spdacquire(sp) } } else { /* make new entry for blocking to send SADB_ACQUIRE. */ - if ((newspacq = key_newspacq(&sp->spidx)) == NULL) + if ((newspacq = key_newspacq(sp->spidx)) == NULL) return ENOBUFS; /* add to acqtree */ LIST_INSERT_HEAD(&spacqtree, newspacq, chain); } +#endif /* create new sadb_msg to reply. */ m = key_setsadbmsg(SADB_X_SPDACQUIRE, 0, 0, 0, 0, 0); @@ -2318,6 +2341,16 @@ key_spdacquire(sp) } result = m; + /* set sadb_x_policy */ + if (sp) { + m = key_setsadbxpolicy(sp->policy, sp->dir, sp->id); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); + } + result->m_pkthdr.len = 0; for (m = result; m; m = m->m_next) result->m_pkthdr.len += m->m_len; @@ -2325,7 +2358,7 @@ key_spdacquire(sp) mtod(result, struct sadb_msg *)->sadb_msg_len = PFKEY_UNIT64(result->m_pkthdr.len); - return key_sendup_mbuf(NULL, m, KEY_SENDUP_REGISTERED); + return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED); fail: if (result) @@ -2352,8 +2385,7 @@ key_spdflush(so, m, mhp) const struct sadb_msghdr *mhp; { struct sadb_msg *newmsg; - struct secpolicy *sp; - u_int dir; + struct secpolicy *sp, *nextsp; /* sanity check */ if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) @@ -2362,12 +2394,20 @@ key_spdflush(so, m, mhp) if (m->m_len != PFKEY_ALIGN8(sizeof(struct sadb_msg))) 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 = TAILQ_FIRST(&sptailq); sp; sp = nextsp) { + nextsp = TAILQ_NEXT(sp, tailq); + if (sp->persist) + continue; + if (sp->state == IPSEC_SPSTATE_DEAD) + continue; + key_sp_dead(sp); + key_sp_unlink(sp); + sp = NULL; } + /* invalidate all cached SPD pointers on pcb */ + ipsec_invalpcbcacheall(); + if (sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) { ipseclog((LOG_DEBUG, "key_spdflush: No more memory.\n")); return key_senderror(so, m, ENOBUFS); @@ -2449,21 +2489,35 @@ key_setdumpsp(sp, type, seq, pid) goto fail; result = m; - m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, - (struct sockaddr *)&sp->spidx.src, sp->spidx.prefs, - sp->spidx.ul_proto); + if (sp->spidx) { + m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&sp->spidx->src, sp->spidx->prefs, + sp->spidx->ul_proto); + if (!m) + goto fail; + m_cat(result, m); + + m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&sp->spidx->dst, sp->spidx->prefd, + sp->spidx->ul_proto); + if (!m) + goto fail; + m_cat(result, m); + } + + m = key_sp2msg(sp); if (!m) goto fail; m_cat(result, m); - m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, - (struct sockaddr *)&sp->spidx.dst, sp->spidx.prefd, - sp->spidx.ul_proto); + 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_sp2msg(sp); + 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); @@ -2524,7 +2578,7 @@ key_getspreqmsglen(sp) } /* - * SADB_SPDEXPIRE processing + * SADB_X_SPDEXPIRE processing * send * * to KMD by PF_KEY. @@ -2584,24 +2638,26 @@ key_spdexpire(sp) 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); + if (sp->spidx) { + 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; + /* 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); } - m_cat(result, m); /* set secpolicy */ m = key_sp2msg(sp); @@ -2693,7 +2749,7 @@ key_delsah(sah) stateidx++) { state = saorder_state_any[stateidx]; - for (sav = (struct secasvar *)LIST_FIRST(&sah->savtree[state]); + for (sav = LIST_FIRST(&sah->savtree[state]); sav != NULL; sav = nextsav) { @@ -2772,7 +2828,7 @@ key_newsav(m, mhp, sah, errp) switch (mhp->msg->sadb_msg_type) { case SADB_GETSPI: - newsav->spi = 0; + key_setspi(newsav, 0); #ifdef IPSEC_DOSEQCHECK /* sync sequence number */ @@ -2793,7 +2849,7 @@ key_newsav(m, mhp, sah, errp) return NULL; } xsa = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA]; - newsav->spi = xsa->sadb_sa_spi; + key_setspi(newsav, xsa->sadb_sa_spi); newsav->seq = mhp->msg->sadb_msg_seq; break; default: @@ -2812,11 +2868,7 @@ key_newsav(m, mhp, sah, errp) } /* reset created */ - { - struct timeval tv; - microtime(&tv); - newsav->created = tv.tv_sec; - } + newsav->created = time_second; newsav->pid = mhp->msg->sadb_msg_pid; @@ -2845,7 +2897,7 @@ key_getsah(saidx) LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) continue; - if (key_cmpsaidx(&sah->saidx, saidx, CMP_REQID)) + if (key_cmpsaidx(&sah->saidx, saidx, CMP_MODE_REQID)) return sah; } @@ -2864,8 +2916,8 @@ key_checkspidup(saidx, spi) struct secasindex *saidx; u_int32_t spi; { - struct secashead *sah; struct secasvar *sav; + u_int stateidx, state; /* check address family */ if (saidx->src.ss_family != saidx->dst.ss_family) { @@ -2874,17 +2926,37 @@ key_checkspidup(saidx, spi) } /* check all SAD */ - LIST_FOREACH(sah, &sahtree, chain) { - if (!key_ismyaddr((struct sockaddr *)&sah->saidx.dst)) + LIST_FOREACH(sav, &spihash[SPIHASH(spi)], spihash) { + if (sav->spi != spi) continue; - sav = key_getsavbyspi(sah, spi); - if (sav != NULL) - return sav; + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_alive); + stateidx++) { + state = saorder_state_alive[stateidx]; + if (sav->state == state && + key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) + return sav; + } } return NULL; } +static void +key_setspi(sav, spi) + struct secasvar *sav; + u_int32_t spi; +{ + int s; + + s = splnet(); + sav->spi = spi; + if (sav->spihash.le_prev || sav->spihash.le_next) + LIST_REMOVE(sav, spihash); + LIST_INSERT_HEAD(&spihash[SPIHASH(spi)], sav, spihash); + splx(s); +} + /* * search SAD litmited alive SA, protocol, SPI. * OUT: @@ -2896,31 +2968,27 @@ key_getsavbyspi(sah, spi) struct secashead *sah; u_int32_t spi; { - struct secasvar *sav; - u_int stateidx, state; - - /* search all status */ - for (stateidx = 0; - stateidx < _ARRAYLEN(saorder_state_alive); - stateidx++) { + struct secasvar *sav, *match; + u_int stateidx, state, matchidx; - state = saorder_state_alive[stateidx]; - LIST_FOREACH(sav, &sah->savtree[state], chain) { - - /* sanity check */ - if (sav->state != state) { - ipseclog((LOG_DEBUG, "key_getsavbyspi: " - "invalid sav->state (queue: %d SA: %d)\n", - state, sav->state)); - continue; + match = NULL; + matchidx = _ARRAYLEN(saorder_state_alive); + LIST_FOREACH(sav, &spihash[SPIHASH(spi)], spihash) { + if (sav->spi != spi) + continue; + if (sav->sah != sah) + continue; + for (stateidx = 0; stateidx < matchidx; stateidx++) { + state = saorder_state_alive[stateidx]; + if (sav->state == state) { + match = sav; + matchidx = stateidx; + break; } - - if (sav->spi == spi) - return sav; } } - return NULL; + return match; } /* @@ -2941,7 +3009,6 @@ key_setsaval(sav, m, mhp) const struct esp_algorithm *algo; #endif int error = 0; - struct timeval tv; /* sanity check */ if (m == NULL || mhp == NULL || mhp->msg == NULL) @@ -3096,8 +3163,7 @@ key_setsaval(sav, m, mhp) } /* reset created */ - microtime(&tv); - sav->created = tv.tv_sec; + sav->created = time_second; /* make lifetime for CURRENT */ KMALLOC(sav->lft_c, struct sadb_lifetime *, @@ -3108,14 +3174,12 @@ key_setsaval(sav, m, mhp) goto fail; } - microtime(&tv); - sav->lft_c->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); sav->lft_c->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; sav->lft_c->sadb_lifetime_allocations = 0; sav->lft_c->sadb_lifetime_bytes = 0; - sav->lft_c->sadb_lifetime_addtime = tv.tv_sec; + sav->lft_c->sadb_lifetime_addtime = time_second; sav->lft_c->sadb_lifetime_usetime = 0; /* lifetimes for HARD and SOFT */ @@ -3135,7 +3199,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]; @@ -3151,7 +3220,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? */ } } @@ -3164,14 +3238,17 @@ key_setsaval(sav, m, mhp) sav->replay = NULL; } if (sav->key_auth != NULL) { + bzero(_KEYBUF(sav->key_auth), _KEYLEN(sav->key_auth)); KFREE(sav->key_auth); sav->key_auth = NULL; } if (sav->key_enc != NULL) { + bzero(_KEYBUF(sav->key_enc), _KEYLEN(sav->key_enc)); KFREE(sav->key_enc); sav->key_enc = NULL; } if (sav->sched) { + bzero(sav->sched, sav->schedlen); KFREE(sav->sched); sav->sched = NULL; } @@ -3411,7 +3488,7 @@ key_setdumpsa(sav, type, satype, seq, pid) case SADB_X_EXT_SA2: m = key_setsadbxsa2(sav->sah->saidx.mode, - sav->replay ? sav->replay->count : 0, + sav->replay ? (sav->replay->count & 0xffffffff) : 0, sav->sah->saidx.reqid); if (!m) goto fail; @@ -3727,6 +3804,40 @@ key_setsadbxsa2(mode, seq, reqid) } /* + * 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 */ static struct mbuf * @@ -3784,7 +3895,7 @@ key_newbuf(src, len) * OUT: 1: true, i.e. my address. * 0: false */ -int +static int key_ismyaddr(sa) struct sockaddr *sa; { @@ -3846,7 +3957,6 @@ key_ismyaddr6(sin6) * XXX Multicast * XXX why do we care about multlicast here while we don't care * about IPv4 multicast?? - * XXX scope */ in6m = NULL; IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m); @@ -3899,8 +4009,8 @@ key_cmpsaidx(saidx0, saidx1, flag) return 0; } else { - /* CMP_MODE_REQID, CMP_REQID, CMP_HEAD */ - if (flag == CMP_MODE_REQID || flag == CMP_REQID) { + /* CMP_MODE_REQID, CMP_HEAD */ + if (flag == CMP_MODE_REQID) { /* * If reqid of SPD is non-zero, unique SA is required. * The result must be of same reqid in this case. @@ -3937,7 +4047,7 @@ key_cmpsaidx(saidx0, saidx1, flag) * 1 : equal * 0 : not equal */ -static int +int key_cmpspidx_exactly(spidx0, spidx1) struct secpolicyindex *spidx0, *spidx1; { @@ -3973,7 +4083,7 @@ key_cmpspidx_exactly(spidx0, spidx1) * 1 : equal * 0 : not equal */ -static int +int key_cmpspidx_withmask(spidx0, spidx1) struct secpolicyindex *spidx0, *spidx1; { @@ -4160,7 +4270,8 @@ key_bbcmp(p1, p2, bits) * XXX: year 2038 problem may remain. */ void -key_timehandler(void) +key_timehandler(arg) + void *arg; { u_int dir; int s; @@ -4181,7 +4292,8 @@ key_timehandler(void) nextsp = LIST_NEXT(sp, chain); if (sp->state == IPSEC_SPSTATE_DEAD) { - key_freesp(sp); + key_sp_unlink(sp); /*XXX*/ + sp = NULL; continue; } @@ -4193,7 +4305,7 @@ key_timehandler(void) tv.tv_sec - sp->created > sp->lifetime) || (sp->validtime && tv.tv_sec - sp->lastused > sp->validtime)) { - sp->state = IPSEC_SPSTATE_DEAD; + key_sp_dead(sp); key_spdexpire(sp); continue; } @@ -4412,10 +4524,11 @@ key_timehandler(void) key_srandom(); } -#ifndef IPSEC_DEBUG2 - /* do exchange to tick time !! */ - (void)timeout((void *)key_timehandler, (void *)0, hz); -#endif /* IPSEC_DEBUG2 */ + /* + * should set timeout based on the most closest timer expiration. + * we don't bother to do that yet. + */ + (void)timeout(key_timehandler, (void *)0, hz); splx(s); return; @@ -4430,7 +4543,7 @@ key_srandom() return; } -u_long +static u_long key_random() { u_long value; @@ -4635,7 +4748,7 @@ key_getspi(so, m, mhp) } /* set spi */ - newsav->spi = htonl(spi); + key_setspi(newsav, htonl(spi)); #ifndef IPSEC_NONBLOCK_ACQUIRE /* delete the entry in acqtree */ @@ -4643,9 +4756,7 @@ key_getspi(so, m, mhp) struct secacq *acq; if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) != NULL) { /* reset counter in order to deletion by timehandler. */ - struct timeval tv; - microtime(&tv); - acq->created = tv.tv_sec; + acq->created = time_second; acq->count = 0; } } @@ -5513,9 +5624,9 @@ key_getcomb_setlifetime(comb) comb->sadb_comb_soft_bytes = 0; comb->sadb_comb_hard_bytes = 0; comb->sadb_comb_hard_addtime = 86400; /* 1 day */ - comb->sadb_comb_soft_addtime = comb->sadb_comb_soft_addtime * 80 / 100; - comb->sadb_comb_soft_usetime = 28800; /* 8 hours */ - comb->sadb_comb_hard_usetime = comb->sadb_comb_hard_usetime * 80 / 100; + comb->sadb_comb_soft_addtime = comb->sadb_comb_hard_addtime * 80 / 100; + comb->sadb_comb_hard_usetime = 28800; /* 8 hours */ + comb->sadb_comb_soft_usetime = comb->sadb_comb_hard_usetime * 80 / 100; } #ifdef IPSEC_ESP @@ -5854,7 +5965,7 @@ key_acquire(saidx, sp) /* set sadb_x_policy */ if (sp) { - m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id); + m = key_setsadbxpolicy(sp->policy, sp->dir, sp->id); if (!m) { error = ENOBUFS; goto fail; @@ -5960,7 +6071,6 @@ key_newacq(saidx) struct secasindex *saidx; { struct secacq *newacq; - struct timeval tv; /* get new entry */ KMALLOC(newacq, struct secacq *, sizeof(struct secacq)); @@ -5973,8 +6083,7 @@ key_newacq(saidx) /* copy secindex */ bcopy(saidx, &newacq->saidx, sizeof(newacq->saidx)); newacq->seq = (acq_seq == ~0 ? 1 : ++acq_seq); - microtime(&tv); - newacq->created = tv.tv_sec; + newacq->created = time_second; newacq->count = 0; return newacq; @@ -6014,7 +6123,9 @@ key_newspacq(spidx) struct secpolicyindex *spidx; { struct secspacq *acq; - struct timeval tv; + + if (!spidx) + return NULL; /* get new entry */ KMALLOC(acq, struct secspacq *, sizeof(struct secspacq)); @@ -6026,9 +6137,8 @@ key_newspacq(spidx) /* copy secindex */ bcopy(spidx, &acq->spidx, sizeof(acq->spidx)); - microtime(&tv); - acq->created = tv.tv_sec; - acq->count = 0; + acq->created = time_second; + acq->count = 1; return acq; } @@ -6039,6 +6149,9 @@ key_getspacq(spidx) { struct secspacq *acq; + if (!spidx) + return NULL; + LIST_FOREACH(acq, &spacqtree, chain) { if (key_cmpspidx_exactly(spidx, &acq->spidx)) return acq; @@ -6067,7 +6180,7 @@ key_acquire2(so, m, mhp) struct mbuf *m; const struct sadb_msghdr *mhp; { - const struct sadb_address *src0, *dst0; + struct sadb_address *src0, *dst0; struct secasindex saidx; struct secashead *sah; u_int16_t proto; @@ -6086,7 +6199,6 @@ key_acquire2(so, m, mhp) if (mhp->msg->sadb_msg_len == PFKEY_UNIT64(sizeof(struct sadb_msg))) { #ifndef IPSEC_NONBLOCK_ACQUIRE struct secacq *acq; - struct timeval tv; /* check sequence number */ if (mhp->msg->sadb_msg_seq == 0) { @@ -6105,8 +6217,7 @@ key_acquire2(so, m, mhp) } /* reset acq counter in order to deletion by timehander. */ - microtime(&tv); - acq->created = tv.tv_sec; + acq->created = time_second; acq->count = 0; #endif m_freem(m); @@ -6422,7 +6533,7 @@ key_expire(sav) /* create SA extension */ m = key_setsadbxsa2(sav->sah->saidx.mode, - sav->replay ? sav->replay->count : 0, + sav->replay ? (sav->replay->count & 0xffffffff) : 0, sav->sah->saidx.reqid); if (!m) { error = ENOBUFS; @@ -6604,7 +6715,6 @@ key_dump(so, m, mhp) u_int8_t satype; u_int8_t state; int cnt; - struct sadb_msg *newmsg; struct mbuf *n; /* sanity check */ @@ -6638,7 +6748,6 @@ key_dump(so, m, mhp) return key_senderror(so, m, ENOENT); /* send this to the userland, one at a time. */ - newmsg = NULL; LIST_FOREACH(sah, &sahtree, chain) { if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC && proto != sah->saidx.proto) @@ -7143,12 +7252,12 @@ key_align(m, mhp) static int key_validate_ext(ext, len) - const struct sadb_ext *ext; + struct sadb_ext *ext; int len; { - const struct sockaddr *sa; + struct sockaddr *sa; enum { NONE, ADDR } checktype = NONE; - int baselen; + int baselen = 0; const int sal = offsetof(struct sockaddr, sa_len) + sizeof(sa->sa_len); if (len != PFKEY_UNUNIT64(ext->sadb_ext_len)) @@ -7173,17 +7282,14 @@ key_validate_ext(ext, len) break; case SADB_EXT_IDENTITY_SRC: case SADB_EXT_IDENTITY_DST: - if (((const struct sadb_ident *)ext)->sadb_ident_type == + if (((struct sadb_ident *)ext)->sadb_ident_type == SADB_X_IDENTTYPE_ADDR) { baselen = PFKEY_ALIGN8(sizeof(struct sadb_ident)); checktype = ADDR; - } else { - baselen = 0; /* XXX pacify gcc-3.1 */ + } else checktype = NONE; - } break; default: - baselen = 0; /* XXX pacify gcc-3.1 */ checktype = NONE; break; } @@ -7192,7 +7298,7 @@ key_validate_ext(ext, len) case NONE: break; case ADDR: - sa = (struct sockaddr *)((uintptr_t)ext + baselen); + sa = (struct sockaddr *)((caddr_t)ext + baselen); if (len < baselen + sal) return EINVAL; if (baselen + PFKEY_ALIGN8(sa->sa_len) != len) @@ -7218,24 +7324,40 @@ key_init() for (i = 0; i <= SADB_SATYPE_MAX; i++) LIST_INIT(®tree[i]); + for (i = 0; i < SPIHASHSIZE; i++) + LIST_INIT(&spihash[i]); + #ifndef IPSEC_NONBLOCK_ACQUIRE LIST_INIT(&acqtree); #endif LIST_INIT(&spacqtree); + TAILQ_INIT(&satailq); + TAILQ_INIT(&sptailq); + /* system default */ #ifdef INET - ip4_def_policy.policy = IPSEC_POLICY_NONE; - ip4_def_policy.refcnt++; /*never reclaim this*/ + ip4_def_policy = key_newsp(0); + if (!ip4_def_policy) + panic("could not initialize IPv4 default security policy"); + ip4_def_policy->state = IPSEC_SPSTATE_ALIVE; + ip4_def_policy->policy = IPSEC_POLICY_NONE; + ip4_def_policy->dir = IPSEC_DIR_ANY; + ip4_def_policy->readonly = 1; + ip4_def_policy->persist = 1; #endif #ifdef INET6 - ip6_def_policy.policy = IPSEC_POLICY_NONE; - ip6_def_policy.refcnt++; /*never reclaim this*/ + ip6_def_policy = key_newsp(0); + if (!ip6_def_policy) + panic("could not initialize IPv6 default security policy"); + ip6_def_policy->state = IPSEC_SPSTATE_ALIVE; + ip6_def_policy->policy = IPSEC_POLICY_NONE; + ip6_def_policy->dir = IPSEC_DIR_ANY; + ip6_def_policy->readonly = 1; + ip6_def_policy->persist = 1; #endif -#ifndef IPSEC_DEBUG2 - timeout((void *)key_timehandler, (void *)0, hz); -#endif /*IPSEC_DEBUG2*/ + timeout(key_timehandler, (void *)0, hz); /* initialize key statistics */ keystat.getspi_count = 1; @@ -7270,8 +7392,6 @@ key_checktunnelsanity(sav, family, src, dst) } #if 0 -#define hostnamelen strlen(hostname) - /* * Get FQDN for the host. * If the administrator configured hostname (by hostname(1)) without @@ -7283,6 +7403,7 @@ key_getfqdn() int i; int hasdot; static char fqdn[MAXHOSTNAMELEN + 1]; + int hostnamelen = strlen(hostname); if (!hostnamelen) return NULL; @@ -7314,22 +7435,20 @@ key_getuserfqdn() struct proc *p = curproc; char *q; - if (p == NULL) - return NULL; - bzero(userfqdn, sizeof(userfqdn)); - if (!(host = key_getfqdn())) { + PROC_LOCK(p); + if (!p || !p->p_pgrp || !p->p_pgrp->pg_session) { PROC_UNLOCK(p); return NULL; } - PROC_LOCK(p); - if (!p->p_pgrp || !p->p_pgrp->pg_session) { + if (!(host = key_getfqdn())) { PROC_UNLOCK(p); return NULL; } /* NOTE: s_login may not be-NUL terminated. */ + bzero(userfqdn, sizeof(userfqdn)); SESS_LOCK(p->p_session); - bcopy(p->p_session->s_login, userfqdn, MAXLOGNAME); + bcopy(p->p_pgrp->pg_session->s_login, userfqdn, MAXLOGNAME); SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); userfqdn[MAXLOGNAME] = '\0'; /* safeguard */ @@ -7383,9 +7502,7 @@ key_sa_recordxfer(sav, m) * <-----> SOFT */ { - struct timeval tv; - microtime(&tv); - sav->lft_c->sadb_lifetime_usetime = tv.tv_sec; + sav->lft_c->sadb_lifetime_usetime = time_second; /* XXX check for expires? */ } @@ -7440,6 +7557,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) diff --git a/sys/netkey/key.h b/sys/netkey/key.h index 4cbe358..6bd1850 100644 --- a/sys/netkey/key.h +++ b/sys/netkey/key.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: key.h,v 1.21 2001/07/27 03:51:30 itojun Exp $ */ +/* $KAME: key.h,v 1.32 2003/09/07 05:25:20 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -35,8 +35,13 @@ #ifdef _KERNEL +#include + extern struct key_cb key_cb; +extern TAILQ_HEAD(_satailq, secasvar) satailq; +extern TAILQ_HEAD(_sptailq, secpolicy) sptailq; + struct secpolicy; struct secpolicyindex; struct ipsecrequest; @@ -46,22 +51,24 @@ struct socket; struct sadb_msg; struct sadb_x_policy; -extern struct secpolicy *key_allocsp(struct secpolicyindex *, u_int); +extern struct secpolicy *key_allocsp(u_int16_t, struct secpolicyindex *, + u_int); extern struct secpolicy *key_gettunnel(struct sockaddr *, struct sockaddr *, struct sockaddr *, struct sockaddr *); extern int key_checkrequest (struct ipsecrequest *isr, struct secasindex *); extern struct secasvar *key_allocsa(u_int, caddr_t, caddr_t, u_int, u_int32_t); extern void key_freesp(struct secpolicy *); -extern void key_freeso(struct socket *); extern void key_freesav(struct secasvar *); -extern struct secpolicy *key_newsp(void); +extern struct secpolicy *key_newsp(u_int32_t); extern struct secpolicy *key_msg2sp(struct sadb_x_policy *, size_t, int *); extern struct mbuf *key_sp2msg(struct secpolicy *); -extern int key_ismyaddr(struct sockaddr *); +extern int key_cmpspidx_exactly + (struct secpolicyindex *, struct secpolicyindex *); +extern int key_cmpspidx_withmask + (struct secpolicyindex *, struct secpolicyindex *); extern int key_spdacquire(struct secpolicy *); -extern void key_timehandler(void); -extern u_long key_random(void); +extern void key_timehandler(void *); extern void key_randomfill(void *, size_t); extern void key_freereg(struct socket *); extern int key_parse(struct mbuf *, struct socket *); diff --git a/sys/netkey/key_debug.c b/sys/netkey/key_debug.c index 35ed02f..a0de240 100644 --- a/sys/netkey/key_debug.c +++ b/sys/netkey/key_debug.c @@ -1,4 +1,4 @@ -/* $KAME: key_debug.c,v 1.26 2001/06/27 10:46:50 sakane Exp $ */ +/* $KAME: key_debug.c,v 1.38 2003/09/06 05:15:44 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -562,10 +562,11 @@ kdebug_secpolicy(sp) if (sp == NULL) panic("kdebug_secpolicy: NULL pointer was passed."); - printf("secpolicy{ refcnt=%u state=%u policy=%u\n", - sp->refcnt, sp->state, sp->policy); + printf("secpolicy{ refcnt=%u state=%u policy=%u dir=%u\n", + sp->refcnt, sp->state, sp->policy, sp->dir); - kdebug_secpolicyindex(&sp->spidx); + if (sp->spidx) + kdebug_secpolicyindex(sp->spidx); switch (sp->policy) { case IPSEC_POLICY_DISCARD: @@ -611,8 +612,8 @@ kdebug_secpolicyindex(spidx) if (spidx == NULL) panic("kdebug_secpolicyindex: NULL pointer was passed."); - printf("secpolicyindex{ dir=%u prefs=%u prefd=%u ul_proto=%u\n", - spidx->dir, spidx->prefs, spidx->prefd, spidx->ul_proto); + printf("secpolicyindex{ prefs=%u prefd=%u ul_proto=%u\n", + spidx->prefs, spidx->prefd, spidx->ul_proto); ipsec_hexdump((caddr_t)&spidx->src, ((struct sockaddr *)&spidx->src)->sa_len); @@ -632,8 +633,7 @@ kdebug_secasindex(saidx) if (saidx == NULL) panic("kdebug_secpolicyindex: NULL pointer was passed."); - printf("secasindex{ mode=%u proto=%u\n", - saidx->mode, saidx->proto); + printf("secasindex{ mode=%u proto=%u\n", saidx->mode, saidx->proto); ipsec_hexdump((caddr_t)&saidx->src, ((struct sockaddr *)&saidx->src)->sa_len); @@ -697,8 +697,9 @@ kdebug_secreplay(rpl) if (rpl == NULL) panic("kdebug_secreplay: NULL pointer was passed."); - printf(" secreplay{ count=%u wsize=%u seq=%u lastseq=%u", - rpl->count, rpl->wsize, rpl->seq, rpl->lastseq); + printf(" secreplay{ count=%llu wsize=%u seq=%llu lastseq=%llu", + (unsigned long long)rpl->count, rpl->wsize, + (unsigned long long)rpl->seq, (unsigned long long)rpl->lastseq); if (rpl->bitmap == NULL) { printf(" }\n"); @@ -736,9 +737,9 @@ kdebug_mbufhdr(m) if (m->m_flags & M_EXT) { printf(" m_ext{ ext_buf:%p ext_free:%p " - "ext_size:%u ref_cnt:%p }\n", + "ext_size:%u }\n", m->m_ext.ext_buf, m->m_ext.ext_free, - m->m_ext.ext_size, m->m_ext.ref_cnt); + m->m_ext.ext_size); } return; diff --git a/sys/netkey/keydb.c b/sys/netkey/keydb.c index 309b512..697b6e5 100644 --- a/sys/netkey/keydb.c +++ b/sys/netkey/keydb.c @@ -1,4 +1,4 @@ -/* $KAME: keydb.c,v 1.64 2000/05/11 17:02:30 itojun Exp $ */ +/* $KAME: keydb.c,v 1.82 2003/09/07 07:47:33 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include @@ -69,17 +70,63 @@ keydb_newsecpolicy() if (!p) return p; bzero(p, sizeof(*p)); + TAILQ_INSERT_TAIL(&sptailq, p, tailq); + return p; } +u_int32_t +keydb_newspid(void) +{ + u_int32_t newid = 0; + static u_int32_t lastalloc = IPSEC_MANUAL_POLICYID_MAX; + struct secpolicy *sp; + + newid = lastalloc + 1; + /* XXX possible infinite loop */ +again: + TAILQ_FOREACH(sp, &sptailq, tailq) { + if (sp->id == newid) + break; + } + if (sp != NULL) { + if (newid + 1 < newid) /* wraparound */ + newid = IPSEC_MANUAL_POLICYID_MAX + 1; + else + newid++; + goto again; + } + lastalloc = newid; + + return newid; +} + void keydb_delsecpolicy(p) struct secpolicy *p; { + TAILQ_REMOVE(&sptailq, p, tailq); + if (p->spidx) + free(p->spidx, M_SECA); free(p, M_SECA); } +int +keydb_setsecpolicyindex(p, idx) + struct secpolicy *p; + struct secpolicyindex *idx; +{ + + if (!p->spidx) + p->spidx = (struct secpolicyindex *)malloc(sizeof(*p->spidx), + M_SECA, M_NOWAIT); + if (!p->spidx) + return ENOMEM; + memcpy(p->spidx, idx, sizeof(*p->spidx)); + return 0; +} + /* * secashead management */ @@ -112,12 +159,36 @@ keydb_delsecashead(p) struct secasvar * keydb_newsecasvar() { - struct secasvar *p; + struct secasvar *p, *q; + static u_int32_t said = 0; p = (struct secasvar *)malloc(sizeof(*p), M_SECA, M_NOWAIT); if (!p) return p; + +again: + said++; + if (said == 0) + said++; + TAILQ_FOREACH(q, &satailq, tailq) { + if (q->id == said) + goto again; + if (TAILQ_NEXT(q, tailq)) { + if (q->id < said && said < TAILQ_NEXT(q, tailq)->id) + break; + if (q->id + 1 < TAILQ_NEXT(q, tailq)->id) { + said = q->id + 1; + break; + } + } + } + bzero(p, sizeof(*p)); + p->id = said; + if (q) + TAILQ_INSERT_AFTER(&satailq, q, p, tailq); + else + TAILQ_INSERT_TAIL(&satailq, p, tailq); return p; } @@ -126,6 +197,8 @@ keydb_delsecasvar(p) struct secasvar *p; { + TAILQ_REMOVE(&satailq, p, tailq); + free(p, M_SECA); } @@ -144,7 +217,7 @@ keydb_newsecreplay(wsize) bzero(p, sizeof(*p)); if (wsize != 0) { - p->bitmap = (caddr_t)malloc(wsize, M_SECA, M_NOWAIT); + p->bitmap = malloc(wsize, M_SECA, M_NOWAIT); if (!p->bitmap) { free(p, M_SECA); return NULL; diff --git a/sys/netkey/keydb.h b/sys/netkey/keydb.h index 0fce183..c9a4bb3 100644 --- a/sys/netkey/keydb.h +++ b/sys/netkey/keydb.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: keydb.h,v 1.14 2000/08/02 17:58:26 sakane Exp $ */ +/* $KAME: keydb.h,v 1.24 2003/09/07 15:12:10 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -72,7 +72,9 @@ struct secashead { /* Security Association */ struct secasvar { + TAILQ_ENTRY(secasvar) tailq; LIST_ENTRY(secasvar) chain; + LIST_ENTRY(secasvar) spihash; int refcnt; /* reference count */ u_int8_t state; /* Status of this Association */ @@ -96,20 +98,22 @@ struct secasvar { struct sadb_lifetime *lft_h; /* HARD lifetime */ struct sadb_lifetime *lft_s; /* SOFT lifetime */ - u_int32_t seq; /* sequence number */ + u_int64_t seq; /* sequence number */ pid_t pid; /* message's pid */ struct secashead *sah; /* back pointer to the secashead */ + + u_int32_t id; /* SA id */ }; /* replay prevention */ struct secreplay { - u_int32_t count; + u_int64_t count; u_int wsize; /* window size, i.g. 4 bytes */ - u_int32_t seq; /* used by sender */ - u_int32_t lastseq; /* used by receiver */ - caddr_t bitmap; /* used by receiver */ - int overflow; /* overflow flag */ + u_int64_t seq; /* used by sender */ + u_int64_t lastseq; /* used by receiver */ + u_int8_t *bitmap; /* used by receiver */ + int overflow; /* what round does the counter take. */ }; /* socket table due to send PF_KEY messages. */ @@ -143,8 +147,13 @@ struct key_cb { }; /* secpolicy */ +struct secpolicy; +struct secpolicyindex; extern struct secpolicy *keydb_newsecpolicy(void); +extern u_int32_t keydb_newspid(void); extern void keydb_delsecpolicy(struct secpolicy *); +extern int keydb_setsecpolicyindex + (struct secpolicy *, struct secpolicyindex *); /* secashead */ extern struct secashead *keydb_newsecashead(void); extern void keydb_delsecashead(struct secashead *); -- cgit v1.1