1 /* $NetBSD: if_gif.c,v 1.83 2014/06/05 23:48:16 rmind Exp $ */ 2 /* $KAME: if_gif.c,v 1.76 2001/08/20 02:01:02 kjc Exp $ */ 3 4 /* 5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.83 2014/06/05 23:48:16 rmind Exp $"); 35 36 #include "opt_inet.h" 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 #include <sys/mbuf.h> 42 #include <sys/socket.h> 43 #include <sys/sockio.h> 44 #include <sys/errno.h> 45 #include <sys/ioctl.h> 46 #include <sys/time.h> 47 #include <sys/syslog.h> 48 #include <sys/proc.h> 49 #include <sys/protosw.h> 50 #include <sys/cpu.h> 51 #include <sys/intr.h> 52 53 #include <net/if.h> 54 #include <net/if_types.h> 55 #include <net/netisr.h> 56 #include <net/route.h> 57 #include <net/bpf.h> 58 59 #include <netinet/in.h> 60 #include <netinet/in_systm.h> 61 #include <netinet/ip.h> 62 #ifdef INET 63 #include <netinet/in_var.h> 64 #endif /* INET */ 65 #include <netinet/in_gif.h> 66 67 #ifdef INET6 68 #ifndef INET 69 #include <netinet/in.h> 70 #endif 71 #include <netinet6/in6_var.h> 72 #include <netinet/ip6.h> 73 #include <netinet6/ip6_var.h> 74 #include <netinet6/in6_gif.h> 75 #include <netinet6/ip6protosw.h> 76 #endif /* INET6 */ 77 78 #include <netinet/ip_encap.h> 79 #include <net/if_gif.h> 80 81 82 #include <net/net_osdep.h> 83 84 void gifattach(int); 85 static void gifintr(void *); 86 87 /* 88 * gif global variable definitions 89 */ 90 LIST_HEAD(, gif_softc) gif_softc_list; /* XXX should be static */ 91 92 static int gif_clone_create(struct if_clone *, int); 93 static int gif_clone_destroy(struct ifnet *); 94 95 static struct if_clone gif_cloner = 96 IF_CLONE_INITIALIZER("gif", gif_clone_create, gif_clone_destroy); 97 98 #ifndef MAX_GIF_NEST 99 /* 100 * This macro controls the upper limitation on nesting of gif tunnels. 101 * Since, setting a large value to this macro with a careless configuration 102 * may introduce system crash, we don't allow any nestings by default. 103 * If you need to configure nested gif tunnels, you can define this macro 104 * in your kernel configuration file. However, if you do so, please be 105 * careful to configure the tunnels so that it won't make a loop. 106 */ 107 #define MAX_GIF_NEST 1 108 #endif 109 static int max_gif_nesting = MAX_GIF_NEST; 110 111 /* ARGSUSED */ 112 void 113 gifattach(int count) 114 { 115 116 LIST_INIT(&gif_softc_list); 117 if_clone_attach(&gif_cloner); 118 } 119 120 static int 121 gif_clone_create(struct if_clone *ifc, int unit) 122 { 123 struct gif_softc *sc; 124 125 sc = malloc(sizeof(struct gif_softc), M_DEVBUF, M_WAITOK|M_ZERO); 126 127 if_initname(&sc->gif_if, ifc->ifc_name, unit); 128 129 gifattach0(sc); 130 131 LIST_INSERT_HEAD(&gif_softc_list, sc, gif_list); 132 return (0); 133 } 134 135 void 136 gifattach0(struct gif_softc *sc) 137 { 138 139 sc->encap_cookie4 = sc->encap_cookie6 = NULL; 140 141 sc->gif_if.if_addrlen = 0; 142 sc->gif_if.if_mtu = GIF_MTU; 143 sc->gif_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; 144 sc->gif_if.if_ioctl = gif_ioctl; 145 sc->gif_if.if_output = gif_output; 146 sc->gif_if.if_type = IFT_GIF; 147 sc->gif_if.if_dlt = DLT_NULL; 148 sc->gif_if.if_softc = sc; 149 IFQ_SET_READY(&sc->gif_if.if_snd); 150 if_attach(&sc->gif_if); 151 if_alloc_sadl(&sc->gif_if); 152 bpf_attach(&sc->gif_if, DLT_NULL, sizeof(u_int)); 153 } 154 155 static int 156 gif_clone_destroy(struct ifnet *ifp) 157 { 158 struct gif_softc *sc = (void *) ifp; 159 160 gif_delete_tunnel(&sc->gif_if); 161 LIST_REMOVE(sc, gif_list); 162 #ifdef INET6 163 encap_detach(sc->encap_cookie6); 164 #endif 165 #ifdef INET 166 encap_detach(sc->encap_cookie4); 167 #endif 168 169 bpf_detach(ifp); 170 if_detach(ifp); 171 rtcache_free(&sc->gif_ro); 172 173 free(sc, M_DEVBUF); 174 175 return (0); 176 } 177 178 #ifdef GIF_ENCAPCHECK 179 int 180 gif_encapcheck(struct mbuf *m, int off, int proto, void *arg) 181 { 182 struct ip ip; 183 struct gif_softc *sc; 184 185 sc = arg; 186 if (sc == NULL) 187 return 0; 188 189 if ((sc->gif_if.if_flags & IFF_UP) == 0) 190 return 0; 191 192 /* no physical address */ 193 if (!sc->gif_psrc || !sc->gif_pdst) 194 return 0; 195 196 switch (proto) { 197 #ifdef INET 198 case IPPROTO_IPV4: 199 break; 200 #endif 201 #ifdef INET6 202 case IPPROTO_IPV6: 203 break; 204 #endif 205 default: 206 return 0; 207 } 208 209 /* Bail on short packets */ 210 KASSERT(m->m_flags & M_PKTHDR); 211 if (m->m_pkthdr.len < sizeof(ip)) 212 return 0; 213 214 m_copydata(m, 0, sizeof(ip), &ip); 215 216 switch (ip.ip_v) { 217 #ifdef INET 218 case 4: 219 if (sc->gif_psrc->sa_family != AF_INET || 220 sc->gif_pdst->sa_family != AF_INET) 221 return 0; 222 return gif_encapcheck4(m, off, proto, arg); 223 #endif 224 #ifdef INET6 225 case 6: 226 if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) 227 return 0; 228 if (sc->gif_psrc->sa_family != AF_INET6 || 229 sc->gif_pdst->sa_family != AF_INET6) 230 return 0; 231 return gif_encapcheck6(m, off, proto, arg); 232 #endif 233 default: 234 return 0; 235 } 236 } 237 #endif 238 239 int 240 gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 241 struct rtentry *rt) 242 { 243 struct gif_softc *sc = ifp->if_softc; 244 int error = 0; 245 static int called = 0; /* XXX: MUTEX */ 246 ALTQ_DECL(struct altq_pktattr pktattr;) 247 int s; 248 249 IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr); 250 251 /* 252 * gif may cause infinite recursion calls when misconfigured. 253 * We'll prevent this by introducing upper limit. 254 * XXX: this mechanism may introduce another problem about 255 * mutual exclusion of the variable CALLED, especially if we 256 * use kernel thread. 257 */ 258 if (++called > max_gif_nesting) { 259 log(LOG_NOTICE, 260 "gif_output: recursively called too many times(%d)\n", 261 called); 262 m_freem(m); 263 error = EIO; /* is there better errno? */ 264 goto end; 265 } 266 267 m->m_flags &= ~(M_BCAST|M_MCAST); 268 if (!(ifp->if_flags & IFF_UP) || 269 sc->gif_psrc == NULL || sc->gif_pdst == NULL) { 270 m_freem(m); 271 error = ENETDOWN; 272 goto end; 273 } 274 275 /* XXX should we check if our outer source is legal? */ 276 277 /* use DLT_NULL encapsulation here to pass inner af type */ 278 M_PREPEND(m, sizeof(int), M_DONTWAIT); 279 if (!m) { 280 error = ENOBUFS; 281 goto end; 282 } 283 *mtod(m, int *) = dst->sa_family; 284 285 /* Clear checksum-offload flags. */ 286 m->m_pkthdr.csum_flags = 0; 287 m->m_pkthdr.csum_data = 0; 288 289 s = splnet(); 290 IFQ_ENQUEUE(&ifp->if_snd, m, &pktattr, error); 291 if (error) { 292 splx(s); 293 goto end; 294 } 295 splx(s); 296 297 softint_schedule(sc->gif_si); 298 error = 0; 299 300 end: 301 called = 0; /* reset recursion counter */ 302 if (error) 303 ifp->if_oerrors++; 304 return error; 305 } 306 307 static void 308 gifintr(void *arg) 309 { 310 struct gif_softc *sc; 311 struct ifnet *ifp; 312 struct mbuf *m; 313 int family; 314 int len; 315 int s; 316 int error; 317 318 sc = arg; 319 ifp = &sc->gif_if; 320 321 /* output processing */ 322 while (1) { 323 s = splnet(); 324 IFQ_DEQUEUE(&sc->gif_if.if_snd, m); 325 splx(s); 326 if (m == NULL) 327 break; 328 329 /* grab and chop off inner af type */ 330 if (sizeof(int) > m->m_len) { 331 m = m_pullup(m, sizeof(int)); 332 if (!m) { 333 ifp->if_oerrors++; 334 continue; 335 } 336 } 337 family = *mtod(m, int *); 338 bpf_mtap(ifp, m); 339 m_adj(m, sizeof(int)); 340 341 len = m->m_pkthdr.len; 342 343 /* dispatch to output logic based on outer AF */ 344 switch (sc->gif_psrc->sa_family) { 345 #ifdef INET 346 case AF_INET: 347 error = in_gif_output(ifp, family, m); 348 break; 349 #endif 350 #ifdef INET6 351 case AF_INET6: 352 error = in6_gif_output(ifp, family, m); 353 break; 354 #endif 355 default: 356 m_freem(m); 357 error = ENETDOWN; 358 break; 359 } 360 361 if (error) 362 ifp->if_oerrors++; 363 else { 364 ifp->if_opackets++; 365 ifp->if_obytes += len; 366 } 367 } 368 } 369 370 void 371 gif_input(struct mbuf *m, int af, struct ifnet *ifp) 372 { 373 pktqueue_t *pktq; 374 size_t pktlen; 375 int s; 376 377 if (ifp == NULL) { 378 /* just in case */ 379 m_freem(m); 380 return; 381 } 382 383 m->m_pkthdr.rcvif = ifp; 384 pktlen = m->m_pkthdr.len; 385 386 bpf_mtap_af(ifp, af, m); 387 388 /* 389 * Put the packet to the network layer input queue according to the 390 * specified address family. Note: we avoid direct call to the 391 * input function of the network layer in order to avoid recursion. 392 * This may be revisited in the future. 393 */ 394 switch (af) { 395 #ifdef INET 396 case AF_INET: 397 pktq = ip_pktq; 398 break; 399 #endif 400 #ifdef INET6 401 case AF_INET6: 402 pktq = ip6_pktq; 403 break; 404 #endif 405 default: 406 m_freem(m); 407 return; 408 } 409 410 s = splnet(); 411 if (__predict_true(pktq_enqueue(pktq, m, 0))) { 412 ifp->if_ibytes += pktlen; 413 ifp->if_ipackets++; 414 } else { 415 m_freem(m); 416 } 417 splx(s); 418 } 419 420 /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */ 421 int 422 gif_ioctl(struct ifnet *ifp, u_long cmd, void *data) 423 { 424 struct gif_softc *sc = ifp->if_softc; 425 struct ifreq *ifr = (struct ifreq*)data; 426 int error = 0, size; 427 struct sockaddr *dst, *src; 428 429 switch (cmd) { 430 case SIOCINITIFADDR: 431 ifp->if_flags |= IFF_UP; 432 break; 433 434 case SIOCADDMULTI: 435 case SIOCDELMULTI: 436 switch (ifr->ifr_addr.sa_family) { 437 #ifdef INET 438 case AF_INET: /* IP supports Multicast */ 439 break; 440 #endif /* INET */ 441 #ifdef INET6 442 case AF_INET6: /* IP6 supports Multicast */ 443 break; 444 #endif /* INET6 */ 445 default: /* Other protocols doesn't support Multicast */ 446 error = EAFNOSUPPORT; 447 break; 448 } 449 break; 450 451 case SIOCSIFMTU: 452 if (ifr->ifr_mtu < GIF_MTU_MIN || ifr->ifr_mtu > GIF_MTU_MAX) 453 return EINVAL; 454 else if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET) 455 error = 0; 456 break; 457 458 #ifdef INET 459 case SIOCSIFPHYADDR: 460 #endif 461 #ifdef INET6 462 case SIOCSIFPHYADDR_IN6: 463 #endif /* INET6 */ 464 case SIOCSLIFPHYADDR: 465 switch (cmd) { 466 #ifdef INET 467 case SIOCSIFPHYADDR: 468 src = (struct sockaddr *) 469 &(((struct in_aliasreq *)data)->ifra_addr); 470 dst = (struct sockaddr *) 471 &(((struct in_aliasreq *)data)->ifra_dstaddr); 472 break; 473 #endif 474 #ifdef INET6 475 case SIOCSIFPHYADDR_IN6: 476 src = (struct sockaddr *) 477 &(((struct in6_aliasreq *)data)->ifra_addr); 478 dst = (struct sockaddr *) 479 &(((struct in6_aliasreq *)data)->ifra_dstaddr); 480 break; 481 #endif 482 case SIOCSLIFPHYADDR: 483 src = (struct sockaddr *) 484 &(((struct if_laddrreq *)data)->addr); 485 dst = (struct sockaddr *) 486 &(((struct if_laddrreq *)data)->dstaddr); 487 break; 488 default: 489 return EINVAL; 490 } 491 492 /* sa_family must be equal */ 493 if (src->sa_family != dst->sa_family) 494 return EINVAL; 495 496 /* validate sa_len */ 497 switch (src->sa_family) { 498 #ifdef INET 499 case AF_INET: 500 if (src->sa_len != sizeof(struct sockaddr_in)) 501 return EINVAL; 502 break; 503 #endif 504 #ifdef INET6 505 case AF_INET6: 506 if (src->sa_len != sizeof(struct sockaddr_in6)) 507 return EINVAL; 508 break; 509 #endif 510 default: 511 return EAFNOSUPPORT; 512 } 513 switch (dst->sa_family) { 514 #ifdef INET 515 case AF_INET: 516 if (dst->sa_len != sizeof(struct sockaddr_in)) 517 return EINVAL; 518 break; 519 #endif 520 #ifdef INET6 521 case AF_INET6: 522 if (dst->sa_len != sizeof(struct sockaddr_in6)) 523 return EINVAL; 524 break; 525 #endif 526 default: 527 return EAFNOSUPPORT; 528 } 529 530 /* check sa_family looks sane for the cmd */ 531 switch (cmd) { 532 case SIOCSIFPHYADDR: 533 if (src->sa_family == AF_INET) 534 break; 535 return EAFNOSUPPORT; 536 #ifdef INET6 537 case SIOCSIFPHYADDR_IN6: 538 if (src->sa_family == AF_INET6) 539 break; 540 return EAFNOSUPPORT; 541 #endif /* INET6 */ 542 case SIOCSLIFPHYADDR: 543 /* checks done in the above */ 544 break; 545 } 546 547 error = gif_set_tunnel(&sc->gif_if, src, dst); 548 break; 549 550 #ifdef SIOCDIFPHYADDR 551 case SIOCDIFPHYADDR: 552 gif_delete_tunnel(&sc->gif_if); 553 break; 554 #endif 555 556 case SIOCGIFPSRCADDR: 557 #ifdef INET6 558 case SIOCGIFPSRCADDR_IN6: 559 #endif /* INET6 */ 560 if (sc->gif_psrc == NULL) { 561 error = EADDRNOTAVAIL; 562 goto bad; 563 } 564 src = sc->gif_psrc; 565 switch (cmd) { 566 #ifdef INET 567 case SIOCGIFPSRCADDR: 568 dst = &ifr->ifr_addr; 569 size = sizeof(ifr->ifr_addr); 570 break; 571 #endif /* INET */ 572 #ifdef INET6 573 case SIOCGIFPSRCADDR_IN6: 574 dst = (struct sockaddr *) 575 &(((struct in6_ifreq *)data)->ifr_addr); 576 size = sizeof(((struct in6_ifreq *)data)->ifr_addr); 577 break; 578 #endif /* INET6 */ 579 default: 580 error = EADDRNOTAVAIL; 581 goto bad; 582 } 583 if (src->sa_len > size) 584 return EINVAL; 585 memcpy(dst, src, src->sa_len); 586 break; 587 588 case SIOCGIFPDSTADDR: 589 #ifdef INET6 590 case SIOCGIFPDSTADDR_IN6: 591 #endif /* INET6 */ 592 if (sc->gif_pdst == NULL) { 593 error = EADDRNOTAVAIL; 594 goto bad; 595 } 596 src = sc->gif_pdst; 597 switch (cmd) { 598 #ifdef INET 599 case SIOCGIFPDSTADDR: 600 dst = &ifr->ifr_addr; 601 size = sizeof(ifr->ifr_addr); 602 break; 603 #endif /* INET */ 604 #ifdef INET6 605 case SIOCGIFPDSTADDR_IN6: 606 dst = (struct sockaddr *) 607 &(((struct in6_ifreq *)data)->ifr_addr); 608 size = sizeof(((struct in6_ifreq *)data)->ifr_addr); 609 break; 610 #endif /* INET6 */ 611 default: 612 error = EADDRNOTAVAIL; 613 goto bad; 614 } 615 if (src->sa_len > size) 616 return EINVAL; 617 memcpy(dst, src, src->sa_len); 618 break; 619 620 case SIOCGLIFPHYADDR: 621 if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) { 622 error = EADDRNOTAVAIL; 623 goto bad; 624 } 625 626 /* copy src */ 627 src = sc->gif_psrc; 628 dst = (struct sockaddr *) 629 &(((struct if_laddrreq *)data)->addr); 630 size = sizeof(((struct if_laddrreq *)data)->addr); 631 if (src->sa_len > size) 632 return EINVAL; 633 memcpy(dst, src, src->sa_len); 634 635 /* copy dst */ 636 src = sc->gif_pdst; 637 dst = (struct sockaddr *) 638 &(((struct if_laddrreq *)data)->dstaddr); 639 size = sizeof(((struct if_laddrreq *)data)->dstaddr); 640 if (src->sa_len > size) 641 return EINVAL; 642 memcpy(dst, src, src->sa_len); 643 break; 644 645 default: 646 return ifioctl_common(ifp, cmd, data); 647 } 648 bad: 649 return error; 650 } 651 652 int 653 gif_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst) 654 { 655 struct gif_softc *sc = ifp->if_softc; 656 struct gif_softc *sc2; 657 struct sockaddr *osrc, *odst; 658 int s; 659 int error; 660 661 s = splsoftnet(); 662 663 LIST_FOREACH(sc2, &gif_softc_list, gif_list) { 664 if (sc2 == sc) 665 continue; 666 if (!sc2->gif_pdst || !sc2->gif_psrc) 667 continue; 668 /* can't configure same pair of address onto two gifs */ 669 if (sockaddr_cmp(sc2->gif_pdst, dst) == 0 && 670 sockaddr_cmp(sc2->gif_psrc, src) == 0) { 671 error = EADDRNOTAVAIL; 672 goto bad; 673 } 674 675 /* XXX both end must be valid? (I mean, not 0.0.0.0) */ 676 } 677 678 if (sc->gif_si) { 679 softint_disestablish(sc->gif_si); 680 sc->gif_si = NULL; 681 } 682 683 /* XXX we can detach from both, but be polite just in case */ 684 if (sc->gif_psrc) 685 switch (sc->gif_psrc->sa_family) { 686 #ifdef INET 687 case AF_INET: 688 (void)in_gif_detach(sc); 689 break; 690 #endif 691 #ifdef INET6 692 case AF_INET6: 693 (void)in6_gif_detach(sc); 694 break; 695 #endif 696 } 697 698 sc->gif_si = softint_establish(SOFTINT_NET, gifintr, sc); 699 if (sc->gif_si == NULL) { 700 error = ENOMEM; 701 goto bad; 702 } 703 704 osrc = sc->gif_psrc; 705 sc->gif_psrc = sockaddr_dup(src, M_WAITOK); 706 707 odst = sc->gif_pdst; 708 sc->gif_pdst = sockaddr_dup(dst, M_WAITOK); 709 710 switch (sc->gif_psrc->sa_family) { 711 #ifdef INET 712 case AF_INET: 713 error = in_gif_attach(sc); 714 break; 715 #endif 716 #ifdef INET6 717 case AF_INET6: 718 error = in6_gif_attach(sc); 719 break; 720 #endif 721 default: 722 error = EINVAL; 723 break; 724 } 725 if (error) { 726 /* rollback */ 727 sockaddr_free(sc->gif_psrc); 728 sockaddr_free(sc->gif_pdst); 729 sc->gif_psrc = osrc; 730 sc->gif_pdst = odst; 731 goto bad; 732 } 733 734 if (osrc) 735 sockaddr_free(osrc); 736 if (odst) 737 sockaddr_free(odst); 738 739 if (sc->gif_psrc && sc->gif_pdst) 740 ifp->if_flags |= IFF_RUNNING; 741 else 742 ifp->if_flags &= ~IFF_RUNNING; 743 splx(s); 744 745 return 0; 746 747 bad: 748 if (sc->gif_si) { 749 softint_disestablish(sc->gif_si); 750 sc->gif_si = NULL; 751 } 752 if (sc->gif_psrc && sc->gif_pdst) 753 ifp->if_flags |= IFF_RUNNING; 754 else 755 ifp->if_flags &= ~IFF_RUNNING; 756 splx(s); 757 758 return error; 759 } 760 761 void 762 gif_delete_tunnel(struct ifnet *ifp) 763 { 764 struct gif_softc *sc = ifp->if_softc; 765 int s; 766 767 s = splsoftnet(); 768 769 if (sc->gif_si) { 770 softint_disestablish(sc->gif_si); 771 sc->gif_si = NULL; 772 } 773 if (sc->gif_psrc) { 774 sockaddr_free(sc->gif_psrc); 775 sc->gif_psrc = NULL; 776 } 777 if (sc->gif_pdst) { 778 sockaddr_free(sc->gif_pdst); 779 sc->gif_pdst = NULL; 780 } 781 /* it is safe to detach from both */ 782 #ifdef INET 783 (void)in_gif_detach(sc); 784 #endif 785 #ifdef INET6 786 (void)in6_gif_detach(sc); 787 #endif 788 789 if (sc->gif_psrc && sc->gif_pdst) 790 ifp->if_flags |= IFF_RUNNING; 791 else 792 ifp->if_flags &= ~IFF_RUNNING; 793 splx(s); 794 } 795