send.c revision 254523
1189251Ssam/*- 2189251Ssam * Copyright (c) 2009-2010 Ana Kukec <anchie@FreeBSD.org> 3281806Srpaulo * All rights reserved. 4189251Ssam * 5252726Srpaulo * Redistribution and use in source and binary forms, with or without 6252726Srpaulo * modification, are permitted provided that the following conditions 7189251Ssam * are met: 8189251Ssam * 1. Redistributions of source code must retain the above copyright 9189251Ssam * notice, this list of conditions and the following disclaimer. 10189251Ssam * 2. Redistributions in binary form must reproduce the above copyright 11189251Ssam * notice, this list of conditions and the following disclaimer in the 12189251Ssam * documentation and/or other materials provided with the distribution. 13189251Ssam * 14189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17214734Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18252726Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19252726Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20252726Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21252726Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22214734Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23252726Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24252726Srpaulo * SUCH DAMAGE. 25252726Srpaulo */ 26252726Srpaulo 27189251Ssam#include <sys/cdefs.h> 28189251Ssam__FBSDID("$FreeBSD: head/sys/netinet6/send.c 254523 2013-08-19 13:27:32Z andre $"); 29289549Srpaulo 30189251Ssam#include <sys/param.h> 31281806Srpaulo#include <sys/kernel.h> 32189251Ssam#include <sys/mbuf.h> 33189251Ssam#include <sys/module.h> 34289549Srpaulo#include <sys/priv.h> 35252726Srpaulo#include <sys/protosw.h> 36252726Srpaulo#include <sys/systm.h> 37189251Ssam#include <sys/socket.h> 38289549Srpaulo#include <sys/sockstate.h> 39252726Srpaulo#include <sys/sockbuf.h> 40189251Ssam#include <sys/socketvar.h> 41189251Ssam#include <sys/types.h> 42189251Ssam 43189251Ssam#include <net/route.h> 44189251Ssam#include <net/if.h> 45189251Ssam#include <net/if_var.h> 46189251Ssam#include <net/vnet.h> 47189251Ssam 48189251Ssam#include <netinet/in.h> 49189251Ssam#include <netinet/ip_var.h> 50189251Ssam#include <netinet/ip6.h> 51189251Ssam#include <netinet/icmp6.h> 52189251Ssam 53189251Ssam#include <netinet6/in6_var.h> 54189251Ssam#include <netinet6/nd6.h> 55189251Ssam#include <netinet6/scope6_var.h> 56189251Ssam#include <netinet6/send.h> 57189251Ssam 58189251Ssamstatic MALLOC_DEFINE(M_SEND, "send", "Secure Neighbour Discovery"); 59189251Ssam 60189251Ssam/* 61189251Ssam * The socket used to communicate with the SeND daemon. 62189251Ssam */ 63189251Ssamstatic VNET_DEFINE(struct socket *, send_so); 64189251Ssam#define V_send_so VNET(send_so) 65189251Ssam 66189251Ssamu_long send_sendspace = 8 * (1024 + sizeof(struct sockaddr_send)); 67189251Ssamu_long send_recvspace = 9216; 68189251Ssam 69189251Ssamstruct mtx send_mtx; 70214734Srpaulo#define SEND_LOCK_INIT() mtx_init(&send_mtx, "send_mtx", NULL, MTX_DEF) 71189251Ssam#define SEND_LOCK() mtx_lock(&send_mtx) 72189251Ssam#define SEND_UNLOCK() mtx_unlock(&send_mtx) 73281806Srpaulo#define SEND_LOCK_DESTROY() mtx_destroy(&send_mtx) 74189251Ssam 75252726Srpaulostatic int 76252726Srpaulosend_attach(struct socket *so, int proto, struct thread *td) 77252726Srpaulo{ 78252726Srpaulo int error; 79289549Srpaulo 80189251Ssam SEND_LOCK(); 81189251Ssam if (V_send_so != NULL) { 82189251Ssam SEND_UNLOCK(); 83189251Ssam return (EEXIST); 84214734Srpaulo } 85281806Srpaulo 86189251Ssam error = priv_check(td, PRIV_NETINET_RAW); 87252726Srpaulo if (error) { 88252726Srpaulo SEND_UNLOCK(); 89252726Srpaulo return(error); 90252726Srpaulo } 91189251Ssam 92252726Srpaulo if (proto != IPPROTO_SEND) { 93252726Srpaulo SEND_UNLOCK(); 94252726Srpaulo return (EPROTONOSUPPORT); 95281806Srpaulo } 96289549Srpaulo error = soreserve(so, send_sendspace, send_recvspace); 97189251Ssam if (error) { 98189251Ssam SEND_UNLOCK(); 99252726Srpaulo return(error); 100252726Srpaulo } 101252726Srpaulo 102252726Srpaulo V_send_so = so; 103252726Srpaulo SEND_UNLOCK(); 104289549Srpaulo 105252726Srpaulo return (0); 106252726Srpaulo} 107189251Ssam 108189251Ssamstatic int 109189251Ssamsend_output(struct mbuf *m, struct ifnet *ifp, int direction) 110189251Ssam{ 111289549Srpaulo struct ip6_hdr *ip6; 112289549Srpaulo struct sockaddr_in6 dst; 113289549Srpaulo struct icmp6_hdr *icmp6; 114189251Ssam int icmp6len; 115189251Ssam 116189251Ssam /* 117189251Ssam * Receive incoming (SeND-protected) or outgoing traffic 118189251Ssam * (SeND-validated) from the SeND user space application. 119189251Ssam */ 120189251Ssam 121252726Srpaulo switch (direction) { 122189251Ssam case SND_IN: 123252726Srpaulo if (m->m_len < (sizeof(struct ip6_hdr) + 124189251Ssam sizeof(struct icmp6_hdr))) { 125189251Ssam m = m_pullup(m, sizeof(struct ip6_hdr) + 126189251Ssam sizeof(struct icmp6_hdr)); 127252726Srpaulo if (!m) 128252726Srpaulo return (ENOBUFS); 129252726Srpaulo } 130252726Srpaulo 131252726Srpaulo /* Before passing off the mbuf record the proper interface. */ 132252726Srpaulo m->m_pkthdr.rcvif = ifp; 133214734Srpaulo 134252726Srpaulo if (m->m_flags & M_PKTHDR) 135252726Srpaulo icmp6len = m->m_pkthdr.len - sizeof(struct ip6_hdr); 136189251Ssam else 137252726Srpaulo panic("Doh! not the first mbuf."); 138252726Srpaulo 139252726Srpaulo ip6 = mtod(m, struct ip6_hdr *); 140214734Srpaulo icmp6 = (struct icmp6_hdr *)(ip6 + 1); 141214734Srpaulo 142252726Srpaulo /* 143252726Srpaulo * Output the packet as icmp6.c:icpm6_input() would do. 144252726Srpaulo * The mbuf is always consumed, so we do not have to 145214734Srpaulo * care about that. 146252726Srpaulo */ 147252726Srpaulo switch (icmp6->icmp6_type) { 148252726Srpaulo case ND_NEIGHBOR_SOLICIT: 149252726Srpaulo nd6_ns_input(m, sizeof(struct ip6_hdr), icmp6len); 150252726Srpaulo break; 151252726Srpaulo case ND_NEIGHBOR_ADVERT: 152252726Srpaulo nd6_na_input(m, sizeof(struct ip6_hdr), icmp6len); 153214734Srpaulo break; 154214734Srpaulo case ND_REDIRECT: 155252726Srpaulo icmp6_redirect_input(m, sizeof(struct ip6_hdr)); 156252726Srpaulo break; 157252726Srpaulo case ND_ROUTER_SOLICIT: 158252726Srpaulo nd6_rs_input(m, sizeof(struct ip6_hdr), icmp6len); 159252726Srpaulo break; 160252726Srpaulo case ND_ROUTER_ADVERT: 161252726Srpaulo nd6_ra_input(m, sizeof(struct ip6_hdr), icmp6len); 162252726Srpaulo break; 163252726Srpaulo default: 164252726Srpaulo return (ENOSYS); 165252726Srpaulo } 166252726Srpaulo return (0); 167252726Srpaulo 168252726Srpaulo case SND_OUT: 169252726Srpaulo if (m->m_len < sizeof(struct ip6_hdr)) { 170252726Srpaulo m = m_pullup(m, sizeof(struct ip6_hdr)); 171252726Srpaulo if (!m) 172252726Srpaulo return (ENOBUFS); 173252726Srpaulo } 174252726Srpaulo ip6 = mtod(m, struct ip6_hdr *); 175252726Srpaulo if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) 176289549Srpaulo m->m_flags |= M_MCAST; 177289549Srpaulo 178252726Srpaulo bzero(&dst, sizeof(dst)); 179252726Srpaulo dst.sin6_family = AF_INET6; 180252726Srpaulo dst.sin6_len = sizeof(dst); 181289549Srpaulo dst.sin6_addr = ip6->ip6_dst; 182252726Srpaulo 183252726Srpaulo m_clrprotoflags(m); /* Avoid confusing lower layers. */ 184281806Srpaulo 185252726Srpaulo /* 186252726Srpaulo * Output the packet as nd6.c:nd6_output_lle() would do. 187252726Srpaulo * The mbuf is always consumed, so we do not have to care 188252726Srpaulo * about that. 189252726Srpaulo * XXX-BZ as we added data, what about fragmenting, 190252726Srpaulo * if now needed? 191252726Srpaulo */ 192252726Srpaulo int error; 193252726Srpaulo error = ((*ifp->if_output)(ifp, m, (struct sockaddr *)&dst, 194252726Srpaulo NULL)); 195252726Srpaulo if (error) 196252726Srpaulo error = ENOENT; 197252726Srpaulo return (error); 198252726Srpaulo 199252726Srpaulo default: 200252726Srpaulo panic("%s: direction %d neither SND_IN nor SND_OUT.", 201252726Srpaulo __func__, direction); 202252726Srpaulo } 203252726Srpaulo} 204252726Srpaulo 205252726Srpaulo/* 206252726Srpaulo * Receive a SeND message from user space to be either send out by the kernel 207252726Srpaulo * or, with SeND ICMPv6 options removed, to be further processed by the icmp6 208252726Srpaulo * input path. 209252726Srpaulo */ 210252726Srpaulostatic int 211252726Srpaulosend_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, 212252726Srpaulo struct mbuf *control, struct thread *td) 213252726Srpaulo{ 214252726Srpaulo struct sockaddr_send *sendsrc; 215252726Srpaulo struct ifnet *ifp; 216252726Srpaulo int error; 217252726Srpaulo 218252726Srpaulo KASSERT(V_send_so == so, ("%s: socket %p not send socket %p", 219252726Srpaulo __func__, so, V_send_so)); 220252726Srpaulo 221252726Srpaulo sendsrc = (struct sockaddr_send *)nam; 222289549Srpaulo ifp = ifnet_byindex_ref(sendsrc->send_ifidx); 223252726Srpaulo if (ifp == NULL) { 224252726Srpaulo error = ENETUNREACH; 225289549Srpaulo goto err; 226289549Srpaulo } 227252726Srpaulo 228252726Srpaulo error = send_output(m, ifp, sendsrc->send_direction); 229252726Srpaulo if_rele(ifp); 230252726Srpaulo m = NULL; 231289549Srpaulo 232252726Srpauloerr: 233252726Srpaulo if (m != NULL) 234281806Srpaulo m_freem(m); 235252726Srpaulo return (error); 236252726Srpaulo} 237252726Srpaulo 238252726Srpaulostatic void 239252726Srpaulosend_close(struct socket *so) 240252726Srpaulo{ 241252726Srpaulo 242252726Srpaulo SEND_LOCK(); 243252726Srpaulo if (V_send_so) 244252726Srpaulo V_send_so = NULL; 245252726Srpaulo SEND_UNLOCK(); 246252726Srpaulo} 247252726Srpaulo 248252726Srpaulo/* 249252726Srpaulo * Send a SeND message to user space, that was either received and has to be 250252726Srpaulo * validated or was about to be send out and has to be handled by the SEND 251252726Srpaulo * daemon adding SeND ICMPv6 options. 252252726Srpaulo */ 253252726Srpaulostatic int 254252726Srpaulosend_input(struct mbuf *m, struct ifnet *ifp, int direction, int msglen __unused) 255252726Srpaulo{ 256252726Srpaulo struct ip6_hdr *ip6; 257214734Srpaulo struct sockaddr_send sendsrc; 258252726Srpaulo 259252726Srpaulo SEND_LOCK(); 260252726Srpaulo if (V_send_so == NULL) { 261252726Srpaulo SEND_UNLOCK(); 262252726Srpaulo return (-1); 263252726Srpaulo } 264252726Srpaulo 265252726Srpaulo /* 266252726Srpaulo * Make sure to clear any possible internally embedded scope before 267252726Srpaulo * passing the packet to user space for SeND cryptographic signature 268252726Srpaulo * validation to succeed. 269252726Srpaulo */ 270252726Srpaulo ip6 = mtod(m, struct ip6_hdr *); 271252726Srpaulo in6_clearscope(&ip6->ip6_src); 272252726Srpaulo in6_clearscope(&ip6->ip6_dst); 273252726Srpaulo 274214734Srpaulo bzero(&sendsrc, sizeof(sendsrc)); 275214734Srpaulo sendsrc.send_len = sizeof(sendsrc); 276252726Srpaulo sendsrc.send_family = AF_INET6; 277252726Srpaulo sendsrc.send_direction = direction; 278252726Srpaulo sendsrc.send_ifidx = ifp->if_index; 279252726Srpaulo 280214734Srpaulo /* 281214734Srpaulo * Send incoming or outgoing traffic to user space either to be 282214734Srpaulo * protected (outgoing) or validated (incoming) according to rfc3971. 283252726Srpaulo */ 284252726Srpaulo SOCKBUF_LOCK(&V_send_so->so_rcv); 285252726Srpaulo if (sbappendaddr_locked(&V_send_so->so_rcv, 286252726Srpaulo (struct sockaddr *)&sendsrc, m, NULL) == 0) { 287252726Srpaulo SOCKBUF_UNLOCK(&V_send_so->so_rcv); 288252726Srpaulo /* XXX stats. */ 289252726Srpaulo m_freem(m); 290252726Srpaulo } else { 291252726Srpaulo sorwakeup_locked(V_send_so); 292252726Srpaulo } 293252726Srpaulo 294252726Srpaulo SEND_UNLOCK(); 295252726Srpaulo return (0); 296252726Srpaulo} 297252726Srpaulo 298252726Srpaulostruct pr_usrreqs send_usrreqs = { 299252726Srpaulo .pru_attach = send_attach, 300252726Srpaulo .pru_send = send_send, 301252726Srpaulo .pru_detach = send_close 302252726Srpaulo}; 303252726Srpaulostruct protosw send_protosw = { 304252726Srpaulo .pr_type = SOCK_RAW, 305252726Srpaulo .pr_flags = PR_ATOMIC|PR_ADDR, 306252726Srpaulo .pr_protocol = IPPROTO_SEND, 307252726Srpaulo .pr_usrreqs = &send_usrreqs 308252726Srpaulo}; 309252726Srpaulo 310214734Srpaulostatic int 311214734Srpaulosend_modevent(module_t mod, int type, void *unused) 312189251Ssam{ 313189251Ssam#ifdef __notyet__ 314214734Srpaulo VNET_ITERATOR_DECL(vnet_iter); 315214734Srpaulo#endif 316214734Srpaulo int error; 317214734Srpaulo 318214734Srpaulo switch (type) { 319214734Srpaulo case MOD_LOAD: 320214734Srpaulo SEND_LOCK_INIT(); 321189251Ssam 322252726Srpaulo error = pf_proto_register(PF_INET6, &send_protosw); 323189251Ssam if (error != 0) { 324189251Ssam printf("%s:%d: MOD_LOAD pf_proto_register(): %d\n", 325189251Ssam __func__, __LINE__, error); 326214734Srpaulo SEND_LOCK_DESTROY(); 327189251Ssam break; 328252726Srpaulo } 329252726Srpaulo send_sendso_input_hook = send_input; 330252726Srpaulo break; 331252726Srpaulo case MOD_UNLOAD: 332252726Srpaulo /* Do not allow unloading w/o locking. */ 333189251Ssam return (EBUSY); 334252726Srpaulo#ifdef __notyet__ 335189251Ssam VNET_LIST_RLOCK_NOSLEEP(); 336289549Srpaulo SEND_LOCK(); 337289549Srpaulo VNET_FOREACH(vnet_iter) { 338289549Srpaulo CURVNET_SET(vnet_iter); 339289549Srpaulo if (V_send_so != NULL) { 340289549Srpaulo CURVNET_RESTORE(); 341289549Srpaulo SEND_UNLOCK(); 342289549Srpaulo VNET_LIST_RUNLOCK_NOSLEEP(); 343252726Srpaulo return (EBUSY); 344252726Srpaulo } 345252726Srpaulo CURVNET_RESTORE(); 346252726Srpaulo } 347252726Srpaulo SEND_UNLOCK(); 348252726Srpaulo VNET_LIST_RUNLOCK_NOSLEEP(); 349252726Srpaulo error = pf_proto_unregister(PF_INET6, IPPROTO_SEND, SOCK_RAW); 350281806Srpaulo if (error == 0) 351252726Srpaulo SEND_LOCK_DESTROY(); 352252726Srpaulo send_sendso_input_hook = NULL; 353252726Srpaulo break; 354252726Srpaulo#endif 355252726Srpaulo default: 356289549Srpaulo error = 0; 357214734Srpaulo break; 358214734Srpaulo } 359214734Srpaulo 360214734Srpaulo return (error); 361214734Srpaulo} 362214734Srpaulo 363289549Srpaulostatic moduledata_t sendmod = { 364214734Srpaulo "send", 365214734Srpaulo send_modevent, 366189251Ssam 0 367189251Ssam}; 368214734Srpaulo 369214734SrpauloDECLARE_MODULE(send, sendmod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); 370214734Srpaulo