if_enc.c revision 1.17
1/* $OpenBSD: if_enc.c,v 1.17 2000/01/07 20:14:51 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 switch (cmd) 399 { 400 case SIOCSIFADDR: 401 return EOPNOTSUPP; 402 403 case SIOCGENCSA: 404 ifsa->sa_spi = enc->sc_spi; 405 ifsa->sa_proto = enc->sc_sproto; 406 bcopy(&enc->sc_dst, &ifsa->sa_dst, enc->sc_dst.sa.sa_len); 407 break; 408 409 case SIOCSENCCLEARSA: 410 /* Check for superuser */ 411 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 412 break; 413 414 if (ifsa->sa_proto == 0) 415 { 416 /* Clear SA if requested */ 417 if (enc->sc_sproto != 0) 418 { 419 s = spltdb(); 420 tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto); 421 if (tdb != NULL) 422 tdb->tdb_interface = 0; 423 splx(s); 424 } 425 426 bzero(&enc->sc_dst, sizeof(union sockaddr_union)); 427 enc->sc_spi = 0; 428 enc->sc_sproto = 0; 429 break; 430 } 431 432 s = spltdb(); 433 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 434 if (tdb == NULL) 435 { 436 splx(s); 437 error = ENOENT; 438 break; 439 } 440 441 tdb->tdb_interface = 0; 442 splx(s); 443 break; 444 445 case SIOCSENCSRCSA: 446 /* Check for superuser */ 447 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 448 break; 449 450 if (ifsa->sa_proto == 0) 451 { 452 error = ENOENT; 453 break; 454 } 455 456 s = spltdb(); 457 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 458 if (tdb == NULL) 459 { 460 splx(s); 461 error = ENOENT; 462 break; 463 } 464 465 /* Is it already bound ? */ 466 if (tdb->tdb_interface) 467 { 468 splx(s); 469 error = EEXIST; 470 break; 471 } 472 473 tdb->tdb_interface = (caddr_t) ifp; 474 splx(s); 475 break; 476 477 case SIOCSENCDSTSA: 478 /* Check for superuser */ 479 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 480 break; 481 482 /* Check for pre-existing TDB */ 483 if (enc->sc_sproto != 0) 484 { 485 error = EEXIST; 486 break; 487 } 488 489 s = spltdb(); 490 491 if (ifsa->sa_proto != 0) 492 { 493 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 494 if (tdb == NULL) 495 { 496 splx(s); 497 error = ENOENT; 498 break; 499 } 500 } 501 else 502 { 503 /* Clear SA if requested */ 504 if (enc->sc_sproto != 0) 505 { 506 tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto); 507 if (tdb != NULL) 508 tdb->tdb_interface = 0; 509 } 510 511 bzero(&enc->sc_dst, sizeof(enc->sc_dst)); 512 enc->sc_spi = 0; 513 enc->sc_sproto = 0; 514 515 splx(s); 516 break; 517 } 518 519#ifdef INET 520 if ((ifsa->sa_dst.sa.sa_family == AF_INET) && 521 (ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in))) 522 { 523 splx(s); 524 error = EINVAL; 525 break; 526 } 527#endif /* INET */ 528 529#ifdef INET6 530 if ((ifsa->sa_dst.sa.sa_family == AF_INET6) && 531 (ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in6))) 532 { 533 splx(s); 534 error = EINVAL; 535 break; 536 } 537#endif /* INET6 */ 538 539 bcopy(&ifsa->sa_dst, &enc->sc_dst, ifsa->sa_dst.sa.sa_len); 540 enc->sc_spi = ifsa->sa_spi; 541 enc->sc_sproto = ifsa->sa_proto; 542 tdb->tdb_interface = (caddr_t) ifp; 543 544 splx(s); 545 break; 546 547 default: 548 error = EINVAL; 549 break; 550 } 551 552 return (error); 553#else /* IPSEC */ 554 return EOPNOTSUPP; 555#endif /* IPSEC */ 556} 557