if_enc.c revision 1.18
1/* $OpenBSD: if_enc.c,v 1.18 2000/01/15 19:38:09 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_DROP(&ifp->if_snd); 147 IF_DEQUEUE(&ifp->if_snd, m); 148 splx(s); 149 150 if (m == NULL) 151 return; 152 else 153 m_freem(m); 154 } 155#else /* IPSEC */ 156 struct enc_softc *enc = ifp->if_softc; 157 int err = 0, protoflag; 158 struct mbuf *mp; 159 struct tdb *tdb; 160 161 /* If the interface is not setup, flush the queue */ 162 if ((enc->sc_spi == 0) && (enc->sc_sproto == 0) && 163 ((enc->sc_dst.sa.sa_family == AF_INET) || 164 (enc->sc_dst.sa.sa_family == AF_INET6))) 165 { 166 DPRINTF(("%s: not initialized with SA\n", ifp->if_xname)); 167 168 for (;;) 169 { 170 s = splimp(); 171 IF_DROP(&ifp->if_snd); 172 IF_DEQUEUE(&ifp->if_snd, m); 173 splx(s); 174 if (m == NULL) 175 return; 176 else 177 m_freem(m); 178 } 179 180 /* Unreachable */ 181 } 182 183 /* Find what type of processing we need to do */ 184 tdb = gettdb(enc->sc_spi, &(enc->sc_dst), enc->sc_sproto); 185 if (tdb == NULL) 186 { 187 DPRINTF(("%s: SA non-existant\n", ifp->if_xname)); 188 189 /* Flush the queue */ 190 for (;;) 191 { 192 s = splimp(); 193 IF_DROP(&ifp->if_snd); 194 IF_DEQUEUE(&ifp->if_snd, m); 195 splx(s); 196 if (m == NULL) 197 return; 198 else 199 m_freem(m); 200 } 201 } 202 203 /* See if we need to notify a key mgmt. daemon to setup SAs */ 204 if (ntohl(enc->sc_spi) == SPI_LOCAL_USE) 205 { 206 /* 207 * XXX Can't do this for now, as there's no way for 208 * XXX key mgmt. to specify link-layer properties 209 * XXX (e.g., encrypt everything on this interface) 210 */ 211#ifdef notyet 212 if (tdb->tdb_satype != SADB_X_SATYPE_BYPASS) 213 pfkeyv2_acquire(tdb, 0); /* No point checking for errors */ 214#endif 215 216 /* Flush the queue */ 217 for (;;) 218 { 219 s = splimp(); 220 IF_DROP(&ifp->if_snd); 221 IF_DEQUEUE(&ifp->if_snd, m); 222 splx(s); 223 if (m == NULL) 224 return; 225 else 226 m_freem(m); 227 } 228 229 /* Unreachable */ 230 } 231 232 /* IPsec-process all packets in the queue */ 233 for (;;) 234 { 235 /* Get a packet from the queue */ 236 s = splimp(); 237 IF_DEQUEUE(&ifp->if_snd, m); 238 splx(s); 239 240 if (m == NULL) /* Empty queue */ 241 return; 242 243 /* First, we encapsulate in etherip */ 244 err = etherip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */ 245 if ((mp == NULL) || err) 246 { 247 /* Just skip this frame */ 248 IF_DROP(&ifp->if_snd); 249 if (mp) 250 m_freem(mp); 251 continue; 252 } 253 else 254 { 255 m = mp; 256 mp = NULL; 257 } 258 259 protoflag = tdb->tdb_dst.sa.sa_family; 260 261 /* IPsec packet processing -- skip encapsulation */ 262 err = ipsp_process_packet(m, &mp, tdb, &protoflag, 1); 263 if ((mp == NULL) || err) 264 { 265 IF_DROP(&ifp->if_snd); 266 if (mp) 267 m_freem(mp); 268 continue; 269 } 270 else 271 { 272 m = mp; 273 mp = NULL; 274 } 275 276#ifdef INET 277 /* Send the packet on its way, no point checking for errors here */ 278 if (protoflag == AF_INET) 279 ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, NULL, NULL); 280#endif /* INET */ 281 282#ifdef INET6 283 /* Send the packet on its way, no point checking for errors here */ 284 if (protoflag == AF_INET6) 285 ip6_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, 286 NULL, NULL); 287#endif /* INET6 */ 288 289 /* XXX Should find a way to avoid bridging-loops, some mbuf flag ? */ 290 } 291#endif /* IPSEC */ 292} 293 294/* 295 * Shamelessly stolen from looutput() 296 */ 297int 298encoutput(ifp, m, dst, rt) 299struct ifnet *ifp; 300register struct mbuf *m; 301struct sockaddr *dst; 302register struct rtentry *rt; 303{ 304 register struct ifqueue *ifq = 0; 305 int s, isr; 306 307 if ((m->m_flags & M_PKTHDR) == 0) 308 panic("encoutput(): no HDR"); 309 310 ifp->if_lastchange = time; 311 m->m_pkthdr.rcvif = ifp; 312 313 if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) 314 { 315 m_freem(m); 316 return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 317 rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 318 } 319 320 ifp->if_opackets++; 321 ifp->if_obytes += m->m_pkthdr.len; 322 323 switch (dst->sa_family) 324 { 325#ifdef INET 326 case AF_INET: 327 ifq = &ipintrq; 328 isr = NETISR_IP; 329 break; 330#endif 331#ifdef INET6 332 case AF_INET6: 333 ifq = &ip6intrq; 334 isr = NETISR_IPV6; 335 break; 336#endif 337#ifdef NS 338 case AF_NS: 339 ifq = &nsintrq; 340 isr = NETISR_NS; 341 break; 342#endif 343#ifdef ISO 344 case AF_ISO: 345 ifq = &clnlintrq; 346 isr = NETISR_ISO; 347 break; 348#endif 349 default: 350 m_freem(m); 351 return (EAFNOSUPPORT); 352 } 353 354 s = splimp(); 355 if (IF_QFULL(ifq)) 356 { 357 IF_DROP(ifq); 358 m_freem(m); 359 splx(s); 360 return (ENOBUFS); 361 } 362 363 IF_ENQUEUE(ifq, m); 364 schednetisr(isr); 365 366 /* Statistics */ 367 ifp->if_ipackets++; 368 ifp->if_ibytes += m->m_pkthdr.len; 369 splx(s); 370 return (0); 371} 372 373/* ARGSUSED */ 374void 375encrtrequest(cmd, rt, sa) 376int cmd; 377struct rtentry *rt; 378struct sockaddr *sa; 379{ 380 if (rt) 381 rt->rt_rmx.rmx_mtu = ENCMTU; 382} 383 384/* ARGSUSED */ 385int 386encioctl(ifp, cmd, data) 387register struct ifnet *ifp; 388u_long cmd; 389caddr_t data; 390{ 391#ifdef IPSEC 392 struct enc_softc *enc = (struct enc_softc *) ifp->if_softc; 393 struct ifsa *ifsa = (struct ifsa *) data; 394 struct proc *prc = curproc; /* XXX */ 395 struct tdb *tdb; 396 int s, error; 397 398 /* 399 * enc0 does not allow binding of SAs, as it's used for all non-bound 400 * SAs. 401 */ 402 if (ifp->if_softc == &encif[0]) 403 return EOPNOTSUPP; 404 405 switch (cmd) 406 { 407 case SIOCSIFADDR: 408 return EOPNOTSUPP; 409 410 case SIOCGENCSA: 411 ifsa->sa_spi = enc->sc_spi; 412 ifsa->sa_proto = enc->sc_sproto; 413 bcopy(&enc->sc_dst, &ifsa->sa_dst, enc->sc_dst.sa.sa_len); 414 break; 415 416 case SIOCSENCCLEARSA: 417 /* Check for superuser */ 418 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 419 break; 420 421 if (ifsa->sa_proto == 0) 422 { 423 /* Clear SA if requested */ 424 if (enc->sc_sproto != 0) 425 { 426 s = spltdb(); 427 tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto); 428 if (tdb != NULL) 429 tdb->tdb_interface = 0; 430 splx(s); 431 } 432 433 bzero(&enc->sc_dst, sizeof(union sockaddr_union)); 434 enc->sc_spi = 0; 435 enc->sc_sproto = 0; 436 break; 437 } 438 439 s = spltdb(); 440 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 441 if (tdb == NULL) 442 { 443 splx(s); 444 error = ENOENT; 445 break; 446 } 447 448 tdb->tdb_interface = 0; 449 splx(s); 450 break; 451 452 case SIOCSENCSRCSA: 453 /* Check for superuser */ 454 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 455 break; 456 457 if (ifsa->sa_proto == 0) 458 { 459 error = ENOENT; 460 break; 461 } 462 463 s = spltdb(); 464 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 465 if (tdb == NULL) 466 { 467 splx(s); 468 error = ENOENT; 469 break; 470 } 471 472 /* Is it already bound ? */ 473 if (tdb->tdb_interface) 474 { 475 splx(s); 476 error = EEXIST; 477 break; 478 } 479 480 tdb->tdb_interface = (caddr_t) ifp; 481 splx(s); 482 break; 483 484 case SIOCSENCDSTSA: 485 /* Check for superuser */ 486 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 487 break; 488 489 /* Check for pre-existing TDB */ 490 if (enc->sc_sproto != 0) 491 { 492 error = EEXIST; 493 break; 494 } 495 496 s = spltdb(); 497 498 if (ifsa->sa_proto != 0) 499 { 500 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 501 if (tdb == NULL) 502 { 503 splx(s); 504 error = ENOENT; 505 break; 506 } 507 } 508 else 509 { 510 /* Clear SA if requested */ 511 if (enc->sc_sproto != 0) 512 { 513 tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto); 514 if (tdb != NULL) 515 tdb->tdb_interface = 0; 516 } 517 518 bzero(&enc->sc_dst, sizeof(enc->sc_dst)); 519 enc->sc_spi = 0; 520 enc->sc_sproto = 0; 521 522 splx(s); 523 break; 524 } 525 526#ifdef INET 527 if ((ifsa->sa_dst.sa.sa_family == AF_INET) && 528 (ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in))) 529 { 530 splx(s); 531 error = EINVAL; 532 break; 533 } 534#endif /* INET */ 535 536#ifdef INET6 537 if ((ifsa->sa_dst.sa.sa_family == AF_INET6) && 538 (ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in6))) 539 { 540 splx(s); 541 error = EINVAL; 542 break; 543 } 544#endif /* INET6 */ 545 546 bcopy(&ifsa->sa_dst, &enc->sc_dst, ifsa->sa_dst.sa.sa_len); 547 enc->sc_spi = ifsa->sa_spi; 548 enc->sc_sproto = ifsa->sa_proto; 549 tdb->tdb_interface = (caddr_t) ifp; 550 551 splx(s); 552 break; 553 554 default: 555 error = EINVAL; 556 break; 557 } 558 559 return (error); 560#else /* IPSEC */ 561 return EOPNOTSUPP; 562#endif /* IPSEC */ 563} 564