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