1/* $NetBSD: natm.c,v 1.23 2011/02/01 19:40:24 chuck Exp $ */ 2 3/* 4 * Copyright (c) 1996 Charles D. Cranor and Washington University. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28/* 29 * natm.c: native mode ATM access (both aal0 and aal5). 30 */ 31 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: natm.c,v 1.23 2011/02/01 19:40:24 chuck Exp $"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/kernel.h> 38#include <sys/domain.h> 39#include <sys/ioctl.h> 40#include <sys/proc.h> 41#include <sys/protosw.h> 42#include <sys/mbuf.h> 43#include <sys/socket.h> 44#include <sys/socketvar.h> 45 46#include <net/if.h> 47#include <net/if_atm.h> 48#include <net/netisr.h> 49#include <net/radix.h> 50#include <net/route.h> 51 52#include <netinet/in.h> 53 54#include <netnatm/natm.h> 55 56u_long natm5_sendspace = 16*1024; 57u_long natm5_recvspace = 16*1024; 58 59u_long natm0_sendspace = 16*1024; 60u_long natm0_recvspace = 16*1024; 61 62/* 63 * user requests 64 */ 65 66int natm_usrreq(so, req, m, nam, control, l) 67 68struct socket *so; 69int req; 70struct mbuf *m, *nam, *control; 71struct lwp *l; 72 73{ 74 int error = 0, s, s2; 75 struct natmpcb *npcb; 76 struct sockaddr_natm *snatm; 77 struct atm_pseudoioctl api; 78 struct atm_pseudohdr *aph; 79 struct atm_rawioctl ario; 80 struct ifnet *ifp; 81 int proto = so->so_proto->pr_protocol; 82 83 s = SPLSOFTNET(); 84 85 npcb = (struct natmpcb *) so->so_pcb; 86 87 if (npcb == NULL && req != PRU_ATTACH) { 88 error = EINVAL; 89 goto done; 90 } 91 92 93 switch (req) { 94 case PRU_ATTACH: /* attach protocol to up */ 95 96 if (npcb) { 97 error = EISCONN; 98 break; 99 } 100 101 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 102 if (proto == PROTO_NATMAAL5) 103 error = soreserve(so, natm5_sendspace, natm5_recvspace); 104 else 105 error = soreserve(so, natm0_sendspace, natm0_recvspace); 106 if (error) 107 break; 108 } 109 110 so->so_pcb = (void *) (npcb = npcb_alloc(M_WAITOK)); 111 npcb->npcb_socket = so; 112 113 break; 114 115 case PRU_DETACH: /* detach protocol from up */ 116 117 /* 118 * we turn on 'drain' *before* we sofree. 119 */ 120 121 npcb_free(npcb, NPCB_DESTROY); /* drain */ 122 so->so_pcb = NULL; 123 /* sofree drops the lock */ 124 sofree(so); 125 mutex_enter(softnet_lock); 126 127 break; 128 129 case PRU_CONNECT: /* establish connection to peer */ 130 131 /* 132 * validate nam and npcb 133 */ 134 135 if (nam->m_len != sizeof(*snatm)) { 136 error = EINVAL; 137 break; 138 } 139 snatm = mtod(nam, struct sockaddr_natm *); 140 if (snatm->snatm_len != sizeof(*snatm) || 141 (npcb->npcb_flags & NPCB_FREE) == 0) { 142 error = EINVAL; 143 break; 144 } 145 if (snatm->snatm_family != AF_NATM) { 146 error = EAFNOSUPPORT; 147 break; 148 } 149 150 snatm->snatm_if[IFNAMSIZ-1] = '\0'; /* XXX ensure null termination 151 since ifunit() uses strcmp */ 152 153 /* 154 * convert interface string to ifp, validate. 155 */ 156 157 ifp = ifunit(snatm->snatm_if); 158 if (ifp == NULL || (ifp->if_flags & IFF_RUNNING) == 0) { 159 error = ENXIO; 160 break; 161 } 162 if (ifp->if_output != atm_output) { 163 error = EAFNOSUPPORT; 164 break; 165 } 166 167 168 /* 169 * register us with the NATM PCB layer 170 */ 171 172 if (npcb_add(npcb, ifp, snatm->snatm_vci, snatm->snatm_vpi) != npcb) { 173 error = EADDRINUSE; 174 break; 175 } 176 177 /* 178 * enable rx 179 */ 180 181 ATM_PH_FLAGS(&api.aph) = (proto == PROTO_NATMAAL5) ? ATM_PH_AAL5 : 0; 182 ATM_PH_VPI(&api.aph) = npcb->npcb_vpi; 183 ATM_PH_SETVCI(&api.aph, npcb->npcb_vci); 184 api.rxhand = npcb; 185 s2 = splnet(); 186 if (ifp->if_ioctl(ifp, SIOCATMENA, &api) != 0) { 187 splx(s2); 188 npcb_free(npcb, NPCB_REMOVE); 189 error = EIO; 190 break; 191 } 192 splx(s2); 193 194 soisconnected(so); 195 196 break; 197 198 case PRU_DISCONNECT: /* disconnect from peer */ 199 200 if ((npcb->npcb_flags & NPCB_CONNECTED) == 0) { 201 printf("natm: disconnected check\n"); 202 error = EIO; 203 break; 204 } 205 ifp = npcb->npcb_ifp; 206 207 /* 208 * disable rx 209 */ 210 211 ATM_PH_FLAGS(&api.aph) = ATM_PH_AAL5; 212 ATM_PH_VPI(&api.aph) = npcb->npcb_vpi; 213 ATM_PH_SETVCI(&api.aph, npcb->npcb_vci); 214 api.rxhand = npcb; 215 s2 = splnet(); 216 ifp->if_ioctl(ifp, SIOCATMDIS, &api); 217 splx(s); 218 219 npcb_free(npcb, NPCB_REMOVE); 220 soisdisconnected(so); 221 222 break; 223 224 case PRU_SHUTDOWN: /* won't send any more data */ 225 socantsendmore(so); 226 break; 227 228 case PRU_SEND: /* send this data */ 229 if (control && control->m_len) { 230 m_freem(control); 231 m_freem(m); 232 error = EINVAL; 233 break; 234 } 235 236 /* 237 * send the data. we must put an atm_pseudohdr on first 238 */ 239 240 M_PREPEND(m, sizeof(*aph), M_WAITOK); 241 if (m == NULL) { 242 error = ENOBUFS; 243 break; 244 } 245 aph = mtod(m, struct atm_pseudohdr *); 246 ATM_PH_VPI(aph) = npcb->npcb_vpi; 247 ATM_PH_SETVCI(aph, npcb->npcb_vci); 248 ATM_PH_FLAGS(aph) = (proto == PROTO_NATMAAL5) ? ATM_PH_AAL5 : 0; 249 250 error = atm_output(npcb->npcb_ifp, m, NULL, NULL); 251 252 break; 253 254 case PRU_SENSE: /* return status into m */ 255 /* return zero? */ 256 break; 257 258 case PRU_PEERADDR: /* fetch peer's address */ 259 snatm = mtod(nam, struct sockaddr_natm *); 260 memset(snatm, 0, sizeof(*snatm)); 261 nam->m_len = snatm->snatm_len = sizeof(*snatm); 262 snatm->snatm_family = AF_NATM; 263 memcpy(snatm->snatm_if, npcb->npcb_ifp->if_xname, sizeof(snatm->snatm_if)); 264 snatm->snatm_vci = npcb->npcb_vci; 265 snatm->snatm_vpi = npcb->npcb_vpi; 266 break; 267 268 case PRU_CONTROL: /* control operations on protocol */ 269 /* 270 * raw atm ioctl. comes in as a SIOCRAWATM. we convert it to 271 * SIOCXRAWATM and pass it to the driver. 272 */ 273 if ((u_long)m == SIOCRAWATM) { 274 if (npcb->npcb_ifp == NULL) { 275 error = ENOTCONN; 276 break; 277 } 278 ario.npcb = npcb; 279 ario.rawvalue = *((int *)nam); 280 error = npcb->npcb_ifp->if_ioctl(npcb->npcb_ifp, SIOCXRAWATM, &ario); 281 if (!error) { 282 if (ario.rawvalue) 283 npcb->npcb_flags |= NPCB_RAW; 284 else 285 npcb->npcb_flags &= ~(NPCB_RAW); 286 } 287 288 break; 289 } 290 291 error = EOPNOTSUPP; 292 break; 293 294 case PRU_BIND: /* bind socket to address */ 295 case PRU_LISTEN: /* listen for connection */ 296 case PRU_ACCEPT: /* accept connection from peer */ 297 case PRU_CONNECT2: /* connect two sockets */ 298 case PRU_ABORT: /* abort (fast DISCONNECT, DETATCH) */ 299 /* (only happens if LISTEN socket) */ 300 case PRU_RCVD: /* have taken data; more room now */ 301 case PRU_FASTTIMO: /* 200ms timeout */ 302 case PRU_SLOWTIMO: /* 500ms timeout */ 303 case PRU_RCVOOB: /* retrieve out of band data */ 304 case PRU_SENDOOB: /* send out of band data */ 305 case PRU_PROTORCV: /* receive from below */ 306 case PRU_PROTOSEND: /* send to below */ 307 case PRU_SOCKADDR: /* fetch socket's address */ 308#ifdef DIAGNOSTIC 309 printf("natm: PRU #%d unsupported\n", req); 310#endif 311 error = EOPNOTSUPP; 312 break; 313 314 default: panic("natm usrreq"); 315 } 316 317done: 318 splx(s); 319 return(error); 320} 321 322/* 323 * natmintr: splsoftnet interrupt 324 * 325 * note: we expect a socket pointer in rcvif rather than an interface 326 * pointer. we can get the interface pointer from the so's PCB if 327 * we really need it. 328 */ 329 330void 331natmintr(void) 332 333{ 334 int s; 335 struct mbuf *m; 336 struct socket *so; 337 struct natmpcb *npcb; 338 339 mutex_enter(softnet_lock); 340next: 341 s = splnet(); 342 IF_DEQUEUE(&natmintrq, m); 343 splx(s); 344 if (m == NULL) { 345 mutex_exit(softnet_lock); 346 return; 347 } 348 349#ifdef DIAGNOSTIC 350 if ((m->m_flags & M_PKTHDR) == 0) 351 panic("natmintr no HDR"); 352#endif 353 354 npcb = (struct natmpcb *) m->m_pkthdr.rcvif; /* XXX: overloaded */ 355 so = npcb->npcb_socket; 356 357 s = splnet(); /* could have atm devs @ different levels */ 358 npcb->npcb_inq--; 359 splx(s); 360 361 if (npcb->npcb_flags & NPCB_DRAIN) { 362 m_freem(m); 363 if (npcb->npcb_inq == 0) 364 free(npcb, M_PCB); /* done! */ 365 goto next; 366 } 367 368 if (npcb->npcb_flags & NPCB_FREE) { 369 m_freem(m); /* drop */ 370 goto next; 371 } 372 373#ifdef NEED_TO_RESTORE_IFP 374 m->m_pkthdr.rcvif = npcb->npcb_ifp; 375#else 376#ifdef DIAGNOSTIC 377m->m_pkthdr.rcvif = NULL; /* null it out to be safe */ 378#endif 379#endif 380 381 if (sbspace(&so->so_rcv) > m->m_pkthdr.len || 382 ((npcb->npcb_flags & NPCB_RAW) != 0 && so->so_rcv.sb_cc < NPCB_RAWCC) ) { 383#ifdef NATM_STAT 384 natm_sookcnt++; 385 natm_sookbytes += m->m_pkthdr.len; 386#endif 387 sbappendrecord(&so->so_rcv, m); 388 sorwakeup(so); 389 } else { 390#ifdef NATM_STAT 391 natm_sodropcnt++; 392 natm_sodropbytes += m->m_pkthdr.len; 393#endif 394 m_freem(m); 395 } 396 397 goto next; 398} 399