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