/* $OpenBSD: pfkey.c,v 1.18 2008/05/23 15:51:12 thib Exp $ */ /* * @(#)COPYRIGHT 1.1 (NRL) 17 January 1995 * * NRL grants permission for redistribution and use in source and binary * forms, with or without modification, of the software and documentation * created at NRL provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgements: * This product includes software developed by the University of * California, Berkeley and its contributors. * This product includes software developed at the Information * Technology Division, US Naval Research Laboratory. * 4. Neither the name of the NRL nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation * are those of the authors and should not be interpreted as representing * official policies, either expressed or implied, of the US Naval * Research Laboratory (NRL). */ /* * Copyright (c) 1995, 1996, 1997, 1998, 1999 Craig Metz. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <sys/types.h> #include <sys/param.h> #include <sys/systm.h> #include <sys/socket.h> #include <sys/mbuf.h> #include <sys/socketvar.h> #include <net/route.h> #include <netinet/ip_ipsp.h> #include <net/pfkeyv2.h> #include <sys/protosw.h> #include <sys/domain.h> #include <net/raw_cb.h> #define PFKEY_PROTOCOL_MAX 3 static struct pfkey_version *pfkey_versions[PFKEY_PROTOCOL_MAX+1] = { NULL, NULL, NULL, NULL }; #define PFKEY_MSG_MAXSZ 4096 struct sockaddr pfkey_addr = { 2, PF_KEY, }; /* static struct domain pfkey_domain; */ static int pfkey_usrreq(struct socket *socket, int req, struct mbuf *mbuf, struct mbuf *nam, struct mbuf *control, struct proc *); static int pfkey_output(struct mbuf *mbuf, struct socket *socket); int pfkey_register(struct pfkey_version *version); int pfkey_unregister(struct pfkey_version *version); int pfkey_sendup(struct socket *socket, struct mbuf *packet, int more); void pfkey_init(void); int pfkey_buildprotosw(void); int pfkey_register(struct pfkey_version *version) { int rval; if ((version->protocol > PFKEY_PROTOCOL_MAX) || (version->protocol < 0)) return (EPROTONOSUPPORT); if (pfkey_versions[version->protocol]) return (EADDRINUSE); pfkey_versions[version->protocol] = version; if ((rval = pfkey_buildprotosw()) != 0) { pfkey_versions[version->protocol] = NULL; return (rval); } return (0); } int pfkey_unregister(struct pfkey_version *version) { int rval; if ((rval = pfkey_buildprotosw()) != 0) return (rval); pfkey_versions[version->protocol] = NULL; return (0); } int pfkey_sendup(struct socket *socket, struct mbuf *packet, int more) { struct mbuf *packet2; int s; if (more) { if (!(packet2 = m_copym2(packet, 0, M_COPYALL, M_DONTWAIT))) return (ENOMEM); } else packet2 = packet; s = spltdb(); if (!sbappendaddr(&socket->so_rcv, &pfkey_addr, packet2, NULL)) { m_freem(packet2); splx(s); return (ENOBUFS); } splx(s); sorwakeup(socket); return (0); } static int pfkey_output(struct mbuf *mbuf, struct socket *socket) { void *message; int error = 0; #ifdef DIAGNOSTIC if (!mbuf || !(mbuf->m_flags & M_PKTHDR)) { error = EINVAL; goto ret; } #endif /* DIAGNOSTIC */ if (mbuf->m_pkthdr.len > PFKEY_MSG_MAXSZ) { error = EMSGSIZE; goto ret; } if (!(message = malloc((unsigned long) mbuf->m_pkthdr.len, M_PFKEY, M_DONTWAIT))) { error = ENOMEM; goto ret; } m_copydata(mbuf, 0, mbuf->m_pkthdr.len, message); error = pfkey_versions[socket->so_proto->pr_protocol]->send(socket, message, mbuf->m_pkthdr.len); ret: if (mbuf) m_freem (mbuf); return (error); } static int pfkey_attach(struct socket *socket, struct mbuf *proto, struct proc *p) { int rval; int s; if (!(socket->so_pcb = malloc(sizeof(struct rawcb), M_PCB, M_DONTWAIT | M_ZERO))) return (ENOMEM); s = splnet(); rval = raw_usrreq(socket, PRU_ATTACH, NULL, proto, NULL, p); splx(s); if (rval) goto ret; ((struct rawcb *)socket->so_pcb)->rcb_faddr = &pfkey_addr; soisconnected(socket); socket->so_options |= SO_USELOOPBACK; if ((rval = pfkey_versions[socket->so_proto->pr_protocol]->create(socket)) != 0) goto ret; return (0); ret: free(socket->so_pcb, M_PCB); return (rval); } static int pfkey_detach(struct socket *socket, struct proc *p) { int rval, i, s; rval = pfkey_versions[socket->so_proto->pr_protocol]->release(socket); s = splnet(); i = raw_usrreq(socket, PRU_DETACH, NULL, NULL, NULL, p); splx(s); if (!rval) rval = i; return (rval); } static int pfkey_usrreq(struct socket *socket, int req, struct mbuf *mbuf, struct mbuf *nam, struct mbuf *control, struct proc *p) { int rval; int s; if ((socket->so_proto->pr_protocol > PFKEY_PROTOCOL_MAX) || (socket->so_proto->pr_protocol < 0) || !pfkey_versions[socket->so_proto->pr_protocol]) return (EPROTONOSUPPORT); switch (req) { case PRU_ATTACH: return (pfkey_attach(socket, nam, p)); case PRU_DETACH: return (pfkey_detach(socket, p)); default: s = splnet(); rval = raw_usrreq(socket, req, mbuf, nam, control, p); splx(s); } return (rval); } static struct domain pfkey_domain = { PF_KEY, "PF_KEY", NULL, /* init */ NULL, /* externalize */ NULL, /* dispose */ NULL, /* protosw */ NULL, /* protoswNPROTOSW */ NULL, /* dom_next */ rn_inithead, /* dom_rtattach */ 16, /* rtoffset */ sizeof(struct sockaddr_encap) /* maxrtkey */ }; static struct protosw pfkey_protosw_template = { SOCK_RAW, &pfkey_domain, -1, /* protocol */ PR_ATOMIC | PR_ADDR, (void *) raw_input, (void *) pfkey_output, (void *) raw_ctlinput, NULL, /* ctloutput */ pfkey_usrreq, NULL, /* init */ NULL, /* fasttimo */ NULL, /* slowtimo */ NULL, /* drain */ NULL /* sysctl */ }; int pfkey_buildprotosw(void) { struct protosw *protosw, *p; int i, j; for (i = j = 0; i <= PFKEY_PROTOCOL_MAX; i++) if (pfkey_versions[i]) j++; if (j) { if (!(protosw = malloc(j * sizeof(struct protosw), M_PFKEY, M_DONTWAIT))) return (ENOMEM); for (i = 0, p = protosw; i <= PFKEY_PROTOCOL_MAX; i++) if (pfkey_versions[i]) { bcopy(&pfkey_protosw_template, p, sizeof(struct protosw)); p->pr_protocol = pfkey_versions[i]->protocol; p->pr_sysctl = pfkey_versions[i]->sysctl; p++; } if (pfkey_domain.dom_protosw) free(pfkey_domain.dom_protosw, M_PFKEY); pfkey_domain.dom_protosw = protosw; pfkey_domain.dom_protoswNPROTOSW = p; } else { if (!(protosw = malloc(sizeof(struct protosw), M_PFKEY, M_DONTWAIT))) return (ENOMEM); bcopy(&pfkey_protosw_template, protosw, sizeof(struct protosw)); if (pfkey_domain.dom_protosw) free(pfkey_domain.dom_protosw, M_PFKEY); pfkey_domain.dom_protosw = protosw; pfkey_domain.dom_protoswNPROTOSW = protosw; } return (0); } void pfkey_init(void) { if (pfkey_buildprotosw() != 0) return; pfkey_domain.dom_next = domains; domains = &pfkey_domain; pfkeyv2_init(); }