if_enc.c revision 1.16
1/* $OpenBSD: if_enc.c,v 1.16 2000/01/02 09:22:58 angelos Exp $ */ 2 3/* 4 * The authors of this code are John Ioannidis (ji@tla.org), 5 * Angelos D. Keromytis (kermit@csd.uch.gr) and 6 * Niels Provos (provos@physnet.uni-hamburg.de). 7 * 8 * This code was written by John Ioannidis for BSD/OS in Athens, Greece, 9 * in November 1995. 10 * 11 * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, 12 * by Angelos D. Keromytis. 13 * 14 * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis 15 * and Niels Provos. 16 * 17 * Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis 18 * and Niels Provos. 19 * 20 * Permission to use, copy, and modify this software without fee 21 * is hereby granted, provided that this entire notice is included in 22 * all copies of any software which is or includes a copy or 23 * modification of this software. 24 * You may use this code under the GNU public license if you so wish. Please 25 * contribute changes back to the authors under this freer than GPL license 26 * so that we may further the use of strong encryption without limitations to 27 * all. 28 * 29 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR 30 * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY 31 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE 32 * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR 33 * PURPOSE. 34 */ 35 36/* 37 * Encapsulation interface driver. 38 */ 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/kernel.h> 43#include <sys/mbuf.h> 44#include <sys/socket.h> 45#include <sys/errno.h> 46#include <sys/ioctl.h> 47#include <sys/proc.h> 48 49#include <net/if.h> 50#include <net/if_types.h> 51#include <net/netisr.h> 52#include <net/route.h> 53#include <net/bpf.h> 54 55#include <netinet/ip_ipsp.h> 56#include <net/if_enc.h> 57 58#ifdef INET 59#include <netinet/in.h> 60#include <netinet/in_systm.h> 61#include <netinet/in_var.h> 62#include <netinet/ip_var.h> 63#include <netinet/ip.h> 64#endif 65 66#ifdef INET6 67#include <netinet6/in6.h> 68#include <netinet6/ip6.h> 69#include <netinet6/ip6_var.h> 70#endif /* INET6 */ 71 72#ifdef ISO 73extern struct ifqueue clnlintrq; 74#endif 75 76#ifdef NS 77extern struct ifqueue nsintrq; 78#endif 79 80#include "bpfilter.h" 81#include "enc.h" 82 83#ifdef ENCDEBUG 84#define DPRINTF(x) do { if (encdebug) printf x ; } while (0) 85#else 86#define DPRINTF(x) 87#endif 88 89#ifndef offsetof 90#define offsetof(s, e) ((int)&((s *)0)->e) 91#endif 92 93struct enc_softc encif[NENC]; 94 95void encattach __P((int)); 96int encoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *, 97 struct rtentry *)); 98int encioctl __P((struct ifnet *, u_long, caddr_t)); 99void encrtrequest __P((int, struct rtentry *, struct sockaddr *)); 100void encstart __P((struct ifnet *)); 101 102extern int ifqmaxlen; 103 104void 105encattach(int nenc) 106{ 107 struct ifnet *ifp; 108 int i; 109 110 bzero(encif, sizeof(encif)); 111 112 for (i = 0; i < NENC; i++) 113 { 114 ifp = &encif[i].sc_if; 115 sprintf(ifp->if_xname, "enc%d", i); 116 ifp->if_softc = &encif[i]; 117 ifp->if_mtu = ENCMTU; 118 ifp->if_ioctl = encioctl; 119 ifp->if_output = encoutput; 120 ifp->if_start = encstart; 121 ifp->if_type = IFT_ENC; 122 ifp->if_snd.ifq_maxlen = ifqmaxlen; 123 ifp->if_hdrlen = ENC_HDRLEN; 124 if_attach(ifp); 125 126#if NBPFILTER > 0 127 bpfattach(&encif[i].sc_if.if_bpf, ifp, DLT_ENC, ENC_HDRLEN); 128#endif 129 } 130} 131 132/* 133 * Start output on the enc interface. 134 */ 135void 136encstart(ifp) 137struct ifnet *ifp; 138{ 139 struct mbuf *m; 140 int s; 141 142#ifndef IPSEC 143 for (;;) 144 { 145 s = splimp(); 146 IF_DEQUEUE(&ifp->if_snd, m); 147 splx(s); 148 149 if (m == NULL) 150 return; 151 else 152 m_freem(m); 153 } 154#else /* IPSEC */ 155 struct enc_softc *enc = ifp->if_softc; 156 int err = 0, protoflag; 157 struct mbuf *mp; 158 struct tdb *tdb; 159 160 /* If the interface is not setup, flush the queue */ 161 if ((enc->sc_spi == 0) && (enc->sc_sproto == 0) && 162 ((enc->sc_dst.sa.sa_family == AF_INET) || 163 (enc->sc_dst.sa.sa_family == AF_INET6))) 164 { 165 DPRINTF(("%s: not initialized with SA\n", ifp->if_xname)); 166 167 for (;;) 168 { 169 s = splimp(); 170 IF_DEQUEUE(&ifp->if_snd, m); 171 splx(s); 172 if (m == NULL) 173 return; 174 else 175 m_freem(m); 176 } 177 178 /* Unreachable */ 179 } 180 181 /* Find what type of processing we need to do */ 182 tdb = gettdb(enc->sc_spi, &(enc->sc_dst), enc->sc_sproto); 183 if (tdb == NULL) 184 { 185 DPRINTF(("%s: SA non-existant\n", ifp->if_xname)); 186 187 /* Flush the queue */ 188 for (;;) 189 { 190 s = splimp(); 191 IF_DEQUEUE(&ifp->if_snd, m); 192 splx(s); 193 if (m == NULL) 194 return; 195 else 196 m_freem(m); 197 } 198 } 199 200 /* See if we need to notify a key mgmt. daemon to setup SAs */ 201 if (ntohl(enc->sc_spi) == SPI_LOCAL_USE) 202 { 203 /* 204 * XXX Can't do this for now, as there's no way for 205 * XXX key mgmt. to specify link-layer properties 206 * XXX (e.g., encrypt everything on this interface) 207 */ 208#ifdef notyet 209 if (tdb->tdb_satype != SADB_X_SATYPE_BYPASS) 210 pfkeyv2_acquire(tdb, 0); /* No point checking for errors */ 211#endif 212 213 /* Flush the queue */ 214 for (;;) 215 { 216 s = splimp(); 217 IF_DEQUEUE(&ifp->if_snd, m); 218 splx(s); 219 if (m == NULL) 220 return; 221 else 222 m_freem(m); 223 } 224 225 /* Unreachable */ 226 } 227 228 /* IPsec-process all packets in the queue */ 229 for (;;) 230 { 231 /* Get a packet from the queue */ 232 s = splimp(); 233 IF_DEQUEUE(&ifp->if_snd, m); 234 splx(s); 235 236 if (m == NULL) /* Empty queue */ 237 return; 238 239 /* First, we encapsulate in etherip */ 240 err = etherip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */ 241 if ((mp == NULL) || err) 242 { 243 /* Just skip this frame */ 244 if (mp) 245 m_freem(mp); 246 continue; 247 } 248 else 249 { 250 m = mp; 251 mp = NULL; 252 } 253 254 protoflag = tdb->tdb_dst.sa.sa_family; 255 256 /* IPsec packet processing -- skip encapsulation */ 257 err = ipsp_process_packet(m, &mp, tdb, &protoflag, 1); 258 if ((mp == NULL) || err) 259 { 260 if (mp) 261 m_freem(mp); 262 continue; 263 } 264 else 265 { 266 m = mp; 267 mp = NULL; 268 } 269 270#ifdef INET 271 /* Send the packet on its way, no point checking for errors here */ 272 if (protoflag == AF_INET) 273 ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, NULL, NULL); 274#endif /* INET */ 275 276#ifdef INET6 277 /* Send the packet on its way, no point checking for errors here */ 278 if (protoflag == AF_INET6) 279 ip6_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, 280 NULL, NULL); 281#endif /* INET6 */ 282 283 /* XXX Should find a way to avoid bridging-loops, some mbuf flag ? */ 284 } 285#endif /* IPSEC */ 286} 287 288/* 289 * Shamelessly stolen from looutput() 290 */ 291int 292encoutput(ifp, m, dst, rt) 293struct ifnet *ifp; 294register struct mbuf *m; 295struct sockaddr *dst; 296register struct rtentry *rt; 297{ 298 register struct ifqueue *ifq = 0; 299 int s, isr; 300 301 if ((m->m_flags & M_PKTHDR) == 0) 302 panic("encoutput(): no HDR"); 303 304 ifp->if_lastchange = time; 305 m->m_pkthdr.rcvif = ifp; 306 307 if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) 308 { 309 m_freem(m); 310 return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 311 rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 312 } 313 314 ifp->if_opackets++; 315 ifp->if_obytes += m->m_pkthdr.len; 316 317 switch (dst->sa_family) 318 { 319#ifdef INET 320 case AF_INET: 321 ifq = &ipintrq; 322 isr = NETISR_IP; 323 break; 324#endif 325#ifdef INET6 326 case AF_INET6: 327 ifq = &ip6intrq; 328 isr = NETISR_IPV6; 329 break; 330#endif 331#ifdef NS 332 case AF_NS: 333 ifq = &nsintrq; 334 isr = NETISR_NS; 335 break; 336#endif 337#ifdef ISO 338 case AF_ISO: 339 ifq = &clnlintrq; 340 isr = NETISR_ISO; 341 break; 342#endif 343 default: 344 m_freem(m); 345 return (EAFNOSUPPORT); 346 } 347 348 s = splimp(); 349 if (IF_QFULL(ifq)) 350 { 351 IF_DROP(ifq); 352 m_freem(m); 353 splx(s); 354 return (ENOBUFS); 355 } 356 357 IF_ENQUEUE(ifq, m); 358 schednetisr(isr); 359 360 /* Statistics */ 361 ifp->if_ipackets++; 362 ifp->if_ibytes += m->m_pkthdr.len; 363 splx(s); 364 return (0); 365} 366 367/* ARGSUSED */ 368void 369encrtrequest(cmd, rt, sa) 370int cmd; 371struct rtentry *rt; 372struct sockaddr *sa; 373{ 374 if (rt) 375 rt->rt_rmx.rmx_mtu = ENCMTU; 376} 377 378/* ARGSUSED */ 379int 380encioctl(ifp, cmd, data) 381register struct ifnet *ifp; 382u_long cmd; 383caddr_t data; 384{ 385#ifdef IPSEC 386 struct enc_softc *enc = (struct enc_softc *) ifp->if_softc; 387 struct ifsa *ifsa = (struct ifsa *) data; 388 struct proc *prc = curproc; /* XXX */ 389 struct tdb *tdb; 390 int s, error; 391 392 switch (cmd) 393 { 394 case SIOCSIFADDR: 395 return EOPNOTSUPP; 396 397 case SIOCGENCSA: 398 ifsa->sa_spi = enc->sc_spi; 399 ifsa->sa_proto = enc->sc_sproto; 400 bcopy(&enc->sc_dst, &ifsa->sa_dst, enc->sc_dst.sa.sa_len); 401 break; 402 403 case SIOCSENCCLEARSA: 404 /* Check for superuser */ 405 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 406 break; 407 408 if (ifsa->sa_proto == 0) 409 { 410 /* Clear SA if requested */ 411 if (enc->sc_sproto != 0) 412 { 413 s = spltdb(); 414 tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto); 415 if (tdb != NULL) 416 tdb->tdb_interface = 0; 417 splx(s); 418 } 419 420 bzero(&enc->sc_dst, sizeof(union sockaddr_union)); 421 enc->sc_spi = 0; 422 enc->sc_sproto = 0; 423 break; 424 } 425 426 s = spltdb(); 427 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 428 if (tdb == NULL) 429 { 430 splx(s); 431 error = ENOENT; 432 break; 433 } 434 435 tdb->tdb_interface = 0; 436 splx(s); 437 break; 438 439 case SIOCSENCSRCSA: 440 /* Check for superuser */ 441 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 442 break; 443 444 if (ifsa->sa_proto == 0) 445 { 446 error = ENOENT; 447 break; 448 } 449 450 s = spltdb(); 451 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 452 if (tdb == NULL) 453 { 454 splx(s); 455 error = ENOENT; 456 break; 457 } 458 459 /* Is it already bound ? */ 460 if (tdb->tdb_interface) 461 { 462 splx(s); 463 error = EEXIST; 464 break; 465 } 466 467 tdb->tdb_interface = (caddr_t) ifp; 468 splx(s); 469 break; 470 471 case SIOCSENCDSTSA: 472 /* Check for superuser */ 473 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 474 break; 475 476 /* Check for pre-existing TDB */ 477 if (enc->sc_sproto != 0) 478 { 479 error = EEXIST; 480 break; 481 } 482 483 s = spltdb(); 484 485 if (ifsa->sa_proto != 0) 486 { 487 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 488 if (tdb == NULL) 489 { 490 splx(s); 491 error = ENOENT; 492 break; 493 } 494 } 495 else 496 { 497 /* Clear SA if requested */ 498 if (enc->sc_sproto != 0) 499 { 500 tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto); 501 if (tdb != NULL) 502 tdb->tdb_interface = 0; 503 } 504 505 bzero(&enc->sc_dst, sizeof(enc->sc_dst)); 506 enc->sc_spi = 0; 507 enc->sc_sproto = 0; 508 509 splx(s); 510 break; 511 } 512 513#ifdef INET 514 if ((ifsa->sa_dst.sa.sa_family == AF_INET) && 515 (ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in))) 516 { 517 splx(s); 518 error = EINVAL; 519 break; 520 } 521#endif /* INET */ 522 523#ifdef INET6 524 if ((ifsa->sa_dst.sa.sa_family == AF_INET6) && 525 (ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in6))) 526 { 527 splx(s); 528 error = EINVAL; 529 break; 530 } 531#endif /* INET6 */ 532 533 bcopy(&ifsa->sa_dst, &enc->sc_dst, ifsa->sa_dst.sa.sa_len); 534 enc->sc_spi = ifsa->sa_spi; 535 enc->sc_sproto = ifsa->sa_proto; 536 tdb->tdb_interface = (caddr_t) ifp; 537 538 splx(s); 539 break; 540 541 default: 542 error = EINVAL; 543 break; 544 } 545 546 return (error); 547#else /* IPSEC */ 548 return EOPNOTSUPP; 549#endif /* IPSEC */ 550} 551