if_enc.c revision 1.30
1/* $OpenBSD: if_enc.c,v 1.30 2000/04/18 06:41:23 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#ifdef notyet 211 /* XXX Currently unsupported */ 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 /* Sanity check */ 244 if ((m->m_flags & M_PKTHDR) == 0) 245 { 246 m_freem(m); 247 continue; 248 } 249 250 ifp->if_opackets++; 251 ifp->if_obytes += m->m_pkthdr.len; 252 253 m->m_pkthdr.rcvif = ifp; 254 mp = NULL; 255 256 /* Encapsulate in etherip or ip-in-ip, depending on interface flag */ 257 if (ifp->if_flags & IFF_LINK0) 258 err = ipip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */ 259 else 260 err = etherip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */ 261 if ((mp == NULL) || err) 262 { 263 /* Just skip this frame */ 264 IF_DROP(&ifp->if_snd); 265 if (mp) 266 m_freem(mp); 267 continue; 268 } 269 else 270 { 271 m = mp; 272 mp = NULL; 273 } 274 275#ifdef INET 276 /* Fix header checksum for IPv4 */ 277 if (tdb->tdb_dst.sa.sa_family == AF_INET) 278 { 279 struct ip *ip; 280 281 ip = mtod(m, struct ip *); 282 ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 283 } 284#endif 285 286 protoflag = tdb->tdb_dst.sa.sa_family; 287 288 /* IPsec packet processing -- skip encapsulation */ 289 ipsp_process_packet(m, tdb, protoflag, 1); 290 291 /* 292 * XXX 293 * Should find a way to avoid bridging/routing-loops, 294 * perhaps use some mbuf flag ? 295 */ 296 } 297#endif /* IPSEC */ 298} 299 300int 301encoutput(ifp, m, dst, rt) 302struct ifnet *ifp; 303register struct mbuf *m; 304struct sockaddr *dst; 305register struct rtentry *rt; 306{ 307 int s; 308 309 if ((m->m_flags & M_PKTHDR) == 0) 310 panic("encoutput(): no HDR"); 311 312 ifp->if_lastchange = time; 313 m->m_pkthdr.rcvif = ifp; 314 315 if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) 316 { 317 m_freem(m); 318 return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 319 rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 320 } 321 322 s = splimp(); 323 if (IF_QFULL(&ifp->if_snd)) { 324 ifp->if_oerrors++; 325 m_freem(m); 326 splx(s); 327 return 0; 328 } 329 330 IF_ENQUEUE(&ifp->if_snd, m); 331 splx(s); 332 333 (ifp->if_start)(ifp); 334 335 return (0); 336} 337 338/* ARGSUSED */ 339void 340encrtrequest(cmd, rt, sa) 341int cmd; 342struct rtentry *rt; 343struct sockaddr *sa; 344{ 345 if (rt) 346 rt->rt_rmx.rmx_mtu = ENCMTU; 347} 348 349/* ARGSUSED */ 350int 351encioctl(ifp, cmd, data) 352register struct ifnet *ifp; 353u_long cmd; 354caddr_t data; 355{ 356#ifdef IPSEC 357 struct enc_softc *enc = (struct enc_softc *) ifp->if_softc; 358 struct ifsa *ifsa = (struct ifsa *) data; 359 struct proc *prc = curproc; /* XXX */ 360 struct tdb *tdb; 361 int s, error = 0; 362 363 /* 364 * enc0 does not allow binding of SAs, as it's used for all non-bound 365 * SAs. 366 */ 367 if (ifp->if_softc == &encif[0]) 368 return EOPNOTSUPP; 369 370 switch (cmd) 371 { 372 case SIOCSIFADDR: 373 case SIOCAIFADDR: 374 case SIOCSIFDSTADDR: 375 case SIOCSIFFLAGS: 376 if (ifp->if_flags & IFF_UP) 377 ifp->if_flags |= IFF_RUNNING; 378 else 379 ifp->if_flags &= ~IFF_RUNNING; 380 break; 381 382 case SIOCGENCSA: 383 ifsa->sa_spi = enc->sc_spi; 384 ifsa->sa_proto = enc->sc_sproto; 385 bcopy(&enc->sc_dst, &ifsa->sa_dst, enc->sc_dst.sa.sa_len); 386 break; 387 388 case SIOCSENCCLEARSA: 389 /* Check for superuser */ 390 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 391 break; 392 393 if (ifsa->sa_proto == 0) 394 { 395 /* Clear SA if requested */ 396 if (enc->sc_sproto != 0) 397 { 398 s = spltdb(); 399 tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto); 400 if (tdb != NULL) 401 tdb->tdb_interface = 0; 402 splx(s); 403 } 404 405 bzero(&enc->sc_dst, sizeof(union sockaddr_union)); 406 enc->sc_spi = 0; 407 enc->sc_sproto = 0; 408 break; 409 } 410 411 s = spltdb(); 412 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 413 if (tdb == NULL) 414 { 415 splx(s); 416 error = ENOENT; 417 break; 418 } 419 420 tdb->tdb_interface = 0; 421 splx(s); 422 break; 423 424 case SIOCSENCSRCSA: 425 /* Check for superuser */ 426 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 427 break; 428 429 if (ifsa->sa_proto == 0) 430 { 431 error = ENOENT; 432 break; 433 } 434 435 s = spltdb(); 436 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 437 if (tdb == NULL) 438 { 439 splx(s); 440 error = ENOENT; 441 break; 442 } 443 444 /* Is it already bound ? */ 445 if (tdb->tdb_interface) 446 { 447 splx(s); 448 error = EEXIST; 449 break; 450 } 451 452 tdb->tdb_interface = (caddr_t) ifp; 453 splx(s); 454 break; 455 456 case SIOCSENCDSTSA: 457 /* Check for superuser */ 458 if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0) 459 break; 460 461 /* Check for pre-existing TDB */ 462 if (enc->sc_sproto != 0) 463 { 464 error = EEXIST; 465 break; 466 } 467 468 s = spltdb(); 469 470 if (ifsa->sa_proto != 0) 471 { 472 tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto); 473 if (tdb == NULL) 474 { 475 splx(s); 476 error = ENOENT; 477 break; 478 } 479 } 480 else 481 { 482 /* Clear SA if requested */ 483 if (enc->sc_sproto != 0) 484 { 485 tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto); 486 if (tdb != NULL) 487 tdb->tdb_interface = 0; 488 } 489 490 bzero(&enc->sc_dst, sizeof(enc->sc_dst)); 491 enc->sc_spi = 0; 492 enc->sc_sproto = 0; 493 494 splx(s); 495 break; 496 } 497 498#ifdef INET 499 if ((ifsa->sa_dst.sa.sa_family == AF_INET) && 500 (ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in))) 501 { 502 splx(s); 503 error = EINVAL; 504 break; 505 } 506#endif /* INET */ 507 508#ifdef INET6 509 if ((ifsa->sa_dst.sa.sa_family == AF_INET6) && 510 (ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in6))) 511 { 512 splx(s); 513 error = EINVAL; 514 break; 515 } 516#endif /* INET6 */ 517 518 bcopy(&ifsa->sa_dst, &enc->sc_dst, ifsa->sa_dst.sa.sa_len); 519 enc->sc_spi = ifsa->sa_spi; 520 enc->sc_sproto = ifsa->sa_proto; 521 tdb->tdb_interface = (caddr_t) ifp; 522 523 splx(s); 524 break; 525 526 default: 527 error = EINVAL; 528 break; 529 } 530 531 return (error); 532#else /* IPSEC */ 533 return EOPNOTSUPP; 534#endif /* IPSEC */ 535} 536