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