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