if_enc.c revision 1.29
1/* $OpenBSD: if_enc.c,v 1.29 2000/04/18 06:35:02 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 m->m_pkthdr.rcvif = ifp; 251 mp = NULL; 252 253 /* Encapsulate in etherip or ip-in-ip, depending on interface flag */ 254 if (ifp->if_flags & IFF_LINK0) 255 err = ipip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */ 256 else 257 err = etherip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */ 258 if ((mp == NULL) || err) 259 { 260 /* Just skip this frame */ 261 IF_DROP(&ifp->if_snd); 262 if (mp) 263 m_freem(mp); 264 continue; 265 } 266 else 267 { 268 m = mp; 269 mp = NULL; 270 } 271 272#ifdef INET 273 /* Fix header checksum for IPv4 */ 274 if (tdb->tdb_dst.sa.sa_family == AF_INET) 275 { 276 struct ip *ip; 277 278 ip = mtod(m, struct ip *); 279 ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 280 } 281#endif 282 283 protoflag = tdb->tdb_dst.sa.sa_family; 284 285 /* IPsec packet processing -- skip encapsulation */ 286 ipsp_process_packet(m, tdb, protoflag, 1); 287 288 /* 289 * XXX 290 * Should find a way to avoid bridging/routing-loops, 291 * perhaps use some mbuf flag ? 292 */ 293 } 294#endif /* IPSEC */ 295} 296 297int 298encoutput(ifp, m, dst, rt) 299struct ifnet *ifp; 300register struct mbuf *m; 301struct sockaddr *dst; 302register struct rtentry *rt; 303{ 304 int s; 305 306 if ((m->m_flags & M_PKTHDR) == 0) 307 panic("encoutput(): no HDR"); 308 309 ifp->if_lastchange = time; 310 m->m_pkthdr.rcvif = ifp; 311 312 if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) 313 { 314 m_freem(m); 315 return (rt->rt_flags & RTF_BLACKHOLE ? 0 : 316 rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); 317 } 318 319 s = splimp(); 320 if (IF_QFULL(&ifp->if_snd)) { 321 ifp->if_oerrors++; 322 m_freem(m); 323 splx(s); 324 return 0; 325 } 326 327 ifp->if_opackets++; 328 ifp->if_obytes += m->m_pkthdr.len; 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