1 /* $OpenBSD: if_gre.c,v 1.80 2016/08/31 15:00:02 reyk Exp $ */ 2 /* $NetBSD: if_gre.c,v 1.9 1999/10/25 19:18:11 drochner Exp $ */ 3 4 /* 5 * Copyright (c) 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Heiko W.Rupp <hwr@pilhuhn.de> 10 * 11 * IPv6-over-GRE contributed by Gert Doering <gert@greenie.muc.de> 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* 36 * Encapsulate L3 protocols into IP, per RFC 1701 and 1702. 37 * See gre(4) for more details. 38 * Also supported: IP in IP encapsulation (proto 55) per RFC 2004. 39 */ 40 41 #include "gre.h" 42 #if NGRE > 0 43 44 #include "bpfilter.h" 45 #include "pf.h" 46 47 #include <sys/param.h> 48 #include <sys/mbuf.h> 49 #include <sys/socket.h> 50 #include <sys/sockio.h> 51 #include <sys/kernel.h> 52 #include <sys/systm.h> 53 #include <sys/timeout.h> 54 55 #include <net/if.h> 56 #include <net/if_types.h> 57 #include <net/route.h> 58 59 #include <netinet/in.h> 60 #include <netinet/ip.h> 61 #include <netinet/ip_var.h> 62 #include <netinet/if_ether.h> 63 64 #if NBPFILTER > 0 65 #include <net/bpf.h> 66 #endif 67 68 #if NPF > 0 69 #include <net/pfvar.h> 70 #endif 71 72 #include <net/if_gre.h> 73 74 #ifndef GRE_RECURSION_LIMIT 75 #define GRE_RECURSION_LIMIT 3 /* How many levels of recursion allowed */ 76 #endif /* GRE_RECURSION_LIMIT */ 77 78 /* 79 * It is not easy to calculate the right value for a GRE MTU. 80 * We leave this task to the admin and use the same default that 81 * other vendors use. 82 */ 83 #define GREMTU 1476 84 85 int gre_clone_create(struct if_clone *, int); 86 int gre_clone_destroy(struct ifnet *); 87 88 struct gre_softc_head gre_softc_list; 89 struct gre_softc_head mobileip_softc_list; 90 91 struct if_clone gre_cloner = 92 IF_CLONE_INITIALIZER("gre", gre_clone_create, gre_clone_destroy); 93 struct if_clone mobileip_cloner = 94 IF_CLONE_INITIALIZER("mobileip", gre_clone_create, gre_clone_destroy); 95 96 /* 97 * We can control the acceptance of GRE and MobileIP packets by 98 * altering the sysctl net.inet.gre.allow and net.inet.mobileip.allow values 99 * respectively. Zero means drop them, all else is acceptance. We can also 100 * control acceptance of WCCPv1-style GRE packets through the 101 * net.inet.gre.wccp value, but be aware it depends upon normal GRE being 102 * allowed as well. 103 * 104 */ 105 int gre_allow = 0; 106 int gre_wccp = 0; 107 int ip_mobile_allow = 0; 108 109 void gre_keepalive(void *); 110 void gre_send_keepalive(void *); 111 void gre_link_state(struct gre_softc *); 112 113 void 114 greattach(int n) 115 { 116 LIST_INIT(&gre_softc_list); 117 LIST_INIT(&mobileip_softc_list); 118 if_clone_attach(&gre_cloner); 119 if_clone_attach(&mobileip_cloner); 120 } 121 122 int 123 gre_clone_create(struct if_clone *ifc, int unit) 124 { 125 struct gre_softc *sc; 126 int s; 127 128 sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO); 129 if (!sc) 130 return (ENOMEM); 131 snprintf(sc->sc_if.if_xname, sizeof sc->sc_if.if_xname, "%s%d", 132 ifc->ifc_name, unit); 133 sc->sc_if.if_softc = sc; 134 sc->sc_if.if_type = IFT_TUNNEL; 135 sc->sc_if.if_addrlen = 0; 136 sc->sc_if.if_hdrlen = 24; /* IP + GRE */ 137 sc->sc_if.if_mtu = GREMTU; 138 sc->sc_if.if_flags = IFF_POINTOPOINT|IFF_MULTICAST; 139 sc->sc_if.if_output = gre_output; 140 sc->sc_if.if_ioctl = gre_ioctl; 141 sc->sc_if.if_rtrequest = p2p_rtrequest; 142 sc->sc_if.if_collisions = 0; 143 sc->sc_if.if_ierrors = 0; 144 sc->sc_if.if_oerrors = 0; 145 sc->sc_if.if_ipackets = 0; 146 sc->sc_if.if_opackets = 0; 147 sc->g_dst.s_addr = sc->g_src.s_addr = INADDR_ANY; 148 sc->sc_ka_state = GRE_STATE_UKNWN; 149 150 if (strcmp("gre", ifc->ifc_name) == 0) { 151 /* GRE encapsulation */ 152 sc->g_proto = IPPROTO_GRE; 153 } else { 154 /* Mobile IP encapsulation */ 155 sc->g_proto = IPPROTO_MOBILE; 156 } 157 158 timeout_set(&sc->sc_ka_hold, gre_keepalive, sc); 159 timeout_set(&sc->sc_ka_snd, gre_send_keepalive, sc); 160 161 if_attach(&sc->sc_if); 162 if_alloc_sadl(&sc->sc_if); 163 164 #if NBPFILTER > 0 165 bpfattach(&sc->sc_if.if_bpf, &sc->sc_if, DLT_LOOP, sizeof(u_int32_t)); 166 #endif 167 s = splnet(); 168 LIST_INSERT_HEAD(&gre_softc_list, sc, sc_list); 169 splx(s); 170 171 return (0); 172 } 173 174 int 175 gre_clone_destroy(struct ifnet *ifp) 176 { 177 struct gre_softc *sc = ifp->if_softc; 178 int s; 179 180 s = splnet(); 181 timeout_del(&sc->sc_ka_snd); 182 timeout_del(&sc->sc_ka_hold); 183 LIST_REMOVE(sc, sc_list); 184 splx(s); 185 186 if_detach(ifp); 187 188 free(sc, M_DEVBUF, sizeof(*sc)); 189 return (0); 190 } 191 192 /* 193 * The output routine. Takes a packet and encapsulates it in the protocol 194 * given by sc->g_proto. See also RFC 1701 and RFC 2004. 195 */ 196 197 int 198 gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 199 struct rtentry *rt) 200 { 201 int error = 0; 202 struct gre_softc *sc = (struct gre_softc *) (ifp->if_softc); 203 struct greip *gh = NULL; 204 struct ip *inp = NULL; 205 u_int8_t ip_tos = 0; 206 u_int16_t etype = 0; 207 struct mobile_h mob_h; 208 struct m_tag *mtag; 209 210 if ((ifp->if_flags & IFF_UP) == 0 || 211 sc->g_src.s_addr == INADDR_ANY || sc->g_dst.s_addr == INADDR_ANY) { 212 m_freem(m); 213 error = ENETDOWN; 214 goto end; 215 } 216 217 #ifdef DIAGNOSTIC 218 if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid)) { 219 printf("%s: trying to send packet on wrong domain. " 220 "if %d vs. mbuf %d, AF %d\n", ifp->if_xname, 221 ifp->if_rdomain, rtable_l2(m->m_pkthdr.ph_rtableid), 222 dst->sa_family); 223 } 224 #endif 225 226 /* Try to limit infinite recursion through misconfiguration. */ 227 for (mtag = m_tag_find(m, PACKET_TAG_GRE, NULL); mtag; 228 mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) { 229 if (!bcmp((caddr_t)(mtag + 1), &ifp, sizeof(struct ifnet *))) { 230 m_freem(m); 231 error = EIO; 232 goto end; 233 } 234 } 235 236 mtag = m_tag_get(PACKET_TAG_GRE, sizeof(struct ifnet *), M_NOWAIT); 237 if (mtag == NULL) { 238 m_freem(m); 239 error = ENOBUFS; 240 goto end; 241 } 242 bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *)); 243 m_tag_prepend(m, mtag); 244 245 m->m_flags &= ~(M_BCAST|M_MCAST); 246 247 #if NBPFILTER > 0 248 if (ifp->if_bpf) 249 bpf_mtap_af(ifp->if_bpf, dst->sa_family, m, BPF_DIRECTION_OUT); 250 #endif 251 252 if (sc->g_proto == IPPROTO_MOBILE) { 253 if (ip_mobile_allow == 0) { 254 m_freem(m); 255 error = EACCES; 256 goto end; 257 } 258 259 if (dst->sa_family == AF_INET) { 260 struct mbuf *m0; 261 int msiz; 262 263 /* 264 * Make sure the complete IP header (with options) 265 * is in the first mbuf. 266 */ 267 if (m->m_len < sizeof(struct ip)) { 268 m = m_pullup(m, sizeof(struct ip)); 269 if (m == NULL) { 270 error = ENOBUFS; 271 goto end; 272 } else 273 inp = mtod(m, struct ip *); 274 275 if (m->m_len < inp->ip_hl << 2) { 276 m = m_pullup(m, inp->ip_hl << 2); 277 if (m == NULL) { 278 error = ENOBUFS; 279 goto end; 280 } 281 } 282 } 283 284 inp = mtod(m, struct ip *); 285 286 bzero(&mob_h, MOB_H_SIZ_L); 287 mob_h.proto = (inp->ip_p) << 8; 288 mob_h.odst = inp->ip_dst.s_addr; 289 inp->ip_dst.s_addr = sc->g_dst.s_addr; 290 291 /* 292 * If the packet comes from our host, we only change 293 * the destination address in the IP header. 294 * Otherwise we need to save and change the source. 295 */ 296 if (inp->ip_src.s_addr == sc->g_src.s_addr) { 297 msiz = MOB_H_SIZ_S; 298 } else { 299 mob_h.proto |= MOB_H_SBIT; 300 mob_h.osrc = inp->ip_src.s_addr; 301 inp->ip_src.s_addr = sc->g_src.s_addr; 302 msiz = MOB_H_SIZ_L; 303 } 304 305 mob_h.proto = htons(mob_h.proto); 306 mob_h.hcrc = gre_in_cksum((u_int16_t *) &mob_h, msiz); 307 308 /* Squeeze in the mobility header */ 309 if ((m->m_data - msiz) < m->m_pktdat) { 310 /* Need new mbuf */ 311 MGETHDR(m0, M_DONTWAIT, MT_HEADER); 312 if (m0 == NULL) { 313 m_freem(m); 314 error = ENOBUFS; 315 goto end; 316 } 317 M_MOVE_HDR(m0, m); 318 319 m0->m_len = msiz + (inp->ip_hl << 2); 320 m0->m_data += max_linkhdr; 321 m0->m_pkthdr.len = m->m_pkthdr.len + msiz; 322 m->m_data += inp->ip_hl << 2; 323 m->m_len -= inp->ip_hl << 2; 324 325 bcopy((caddr_t) inp, mtod(m0, caddr_t), 326 sizeof(struct ip)); 327 328 m0->m_next = m; 329 m = m0; 330 } else { /* we have some space left in the old one */ 331 m->m_data -= msiz; 332 m->m_len += msiz; 333 m->m_pkthdr.len += msiz; 334 bcopy(inp, mtod(m, caddr_t), 335 inp->ip_hl << 2); 336 } 337 338 /* Copy Mobility header */ 339 inp = mtod(m, struct ip *); 340 bcopy(&mob_h, (caddr_t)(inp + 1), (unsigned) msiz); 341 inp->ip_len = htons(ntohs(inp->ip_len) + msiz); 342 } else { /* AF_INET */ 343 m_freem(m); 344 error = EINVAL; 345 goto end; 346 } 347 } else if (sc->g_proto == IPPROTO_GRE) { 348 if (gre_allow == 0) { 349 m_freem(m); 350 error = EACCES; 351 goto end; 352 } 353 354 switch(dst->sa_family) { 355 case AF_INET: 356 if (m->m_len < sizeof(struct ip)) { 357 m = m_pullup(m, sizeof(struct ip)); 358 if (m == NULL) { 359 error = ENOBUFS; 360 goto end; 361 } 362 } 363 364 inp = mtod(m, struct ip *); 365 ip_tos = inp->ip_tos; 366 etype = ETHERTYPE_IP; 367 break; 368 #ifdef INET6 369 case AF_INET6: 370 etype = ETHERTYPE_IPV6; 371 break; 372 #endif 373 #ifdef MPLS 374 case AF_MPLS: 375 if (m->m_flags & (M_BCAST | M_MCAST)) 376 etype = ETHERTYPE_MPLS_MCAST; 377 else 378 etype = ETHERTYPE_MPLS; 379 break; 380 #endif 381 default: 382 m_freem(m); 383 error = EAFNOSUPPORT; 384 goto end; 385 } 386 387 M_PREPEND(m, sizeof(struct greip), M_DONTWAIT); 388 } else { 389 m_freem(m); 390 error = EINVAL; 391 goto end; 392 } 393 394 if (m == NULL) { 395 error = ENOBUFS; 396 goto end; 397 } 398 399 gh = mtod(m, struct greip *); 400 if (sc->g_proto == IPPROTO_GRE) { 401 /* We don't support any GRE flags for now */ 402 403 bzero((void *) &gh->gi_g, sizeof(struct gre_h)); 404 gh->gi_ptype = htons(etype); 405 } 406 407 gh->gi_pr = sc->g_proto; 408 if (sc->g_proto != IPPROTO_MOBILE) { 409 gh->gi_src = sc->g_src; 410 gh->gi_dst = sc->g_dst; 411 ((struct ip *) gh)->ip_hl = (sizeof(struct ip)) >> 2; 412 ((struct ip *) gh)->ip_ttl = ip_defttl; 413 ((struct ip *) gh)->ip_tos = ip_tos; 414 gh->gi_len = htons(m->m_pkthdr.len); 415 } 416 417 ifp->if_opackets++; 418 ifp->if_obytes += m->m_pkthdr.len; 419 420 421 m->m_pkthdr.ph_rtableid = sc->g_rtableid; 422 423 #if NPF > 0 424 pf_pkt_addr_changed(m); 425 #endif 426 427 /* Send it off */ 428 error = ip_output(m, NULL, &sc->route, 0, NULL, NULL, 0); 429 end: 430 if (error) 431 ifp->if_oerrors++; 432 return (error); 433 } 434 435 int 436 gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 437 { 438 439 struct ifreq *ifr = (struct ifreq *)data; 440 struct if_laddrreq *lifr = (struct if_laddrreq *)data; 441 struct ifkalivereq *ikar = (struct ifkalivereq *)data; 442 struct gre_softc *sc = ifp->if_softc; 443 int s; 444 struct sockaddr_in si; 445 int error = 0; 446 struct proc *prc = curproc; /* XXX */ 447 448 s = splnet(); 449 switch(cmd) { 450 case SIOCSIFADDR: 451 ifp->if_flags |= IFF_UP; 452 break; 453 case SIOCSIFDSTADDR: 454 break; 455 case SIOCSIFFLAGS: 456 break; 457 case SIOCSIFMTU: 458 if (ifr->ifr_mtu < 576) { 459 error = EINVAL; 460 break; 461 } 462 ifp->if_mtu = ifr->ifr_mtu; 463 break; 464 case SIOCGIFMTU: 465 ifr->ifr_mtu = sc->sc_if.if_mtu; 466 break; 467 case SIOCGIFHARDMTU: 468 ifr->ifr_hardmtu = sc->sc_if.if_hardmtu; 469 break; 470 case SIOCADDMULTI: 471 case SIOCDELMULTI: 472 break; 473 case SIOCSETKALIVE: 474 if ((error = suser(prc, 0)) != 0) 475 break; 476 if (ikar->ikar_timeo < 0 || ikar->ikar_timeo > 86400 || 477 ikar->ikar_cnt < 0 || ikar->ikar_cnt > 256) { 478 error = EINVAL; 479 break; 480 } 481 sc->sc_ka_timout = ikar->ikar_timeo; 482 sc->sc_ka_cnt = ikar->ikar_cnt; 483 if (sc->sc_ka_timout == 0 || sc->sc_ka_cnt == 0) { 484 sc->sc_ka_timout = 0; 485 sc->sc_ka_cnt = 0; 486 sc->sc_ka_state = GRE_STATE_UKNWN; 487 gre_link_state(sc); 488 break; 489 } 490 if (!timeout_pending(&sc->sc_ka_snd)) { 491 sc->sc_ka_holdmax = sc->sc_ka_cnt; 492 timeout_add(&sc->sc_ka_snd, 1); 493 timeout_add_sec(&sc->sc_ka_hold, sc->sc_ka_timout * 494 sc->sc_ka_cnt); 495 } 496 break; 497 case SIOCGETKALIVE: 498 ikar->ikar_timeo = sc->sc_ka_timout; 499 ikar->ikar_cnt = sc->sc_ka_cnt; 500 break; 501 case SIOCSLIFPHYADDR: 502 if ((error = suser(prc, 0)) != 0) 503 break; 504 if (lifr->addr.ss_family != AF_INET || 505 lifr->dstaddr.ss_family != AF_INET) { 506 error = EAFNOSUPPORT; 507 break; 508 } 509 if (lifr->addr.ss_len != sizeof(si) || 510 lifr->dstaddr.ss_len != sizeof(si)) { 511 error = EINVAL; 512 break; 513 } 514 sc->g_src = ((struct sockaddr_in *)&lifr->addr)->sin_addr; 515 sc->g_dst = ((struct sockaddr_in *)&lifr->dstaddr)->sin_addr; 516 recompute: 517 if ((sc->g_src.s_addr != INADDR_ANY) && 518 (sc->g_dst.s_addr != INADDR_ANY)) { 519 if (sc->route.ro_rt != NULL) { 520 rtfree(sc->route.ro_rt); 521 sc->route.ro_rt = NULL; 522 } 523 /* ip_output() will do the lookup */ 524 bzero(&sc->route, sizeof(sc->route)); 525 ifp->if_flags |= IFF_UP; 526 } 527 break; 528 case SIOCDIFPHYADDR: 529 if ((error = suser(prc, 0)) != 0) 530 break; 531 sc->g_src.s_addr = INADDR_ANY; 532 sc->g_dst.s_addr = INADDR_ANY; 533 break; 534 case SIOCGLIFPHYADDR: 535 if (sc->g_src.s_addr == INADDR_ANY || 536 sc->g_dst.s_addr == INADDR_ANY) { 537 error = EADDRNOTAVAIL; 538 break; 539 } 540 bzero(&si, sizeof(si)); 541 si.sin_family = AF_INET; 542 si.sin_len = sizeof(struct sockaddr_in); 543 si.sin_addr.s_addr = sc->g_src.s_addr; 544 memcpy(&lifr->addr, &si, sizeof(si)); 545 si.sin_addr.s_addr = sc->g_dst.s_addr; 546 memcpy(&lifr->dstaddr, &si, sizeof(si)); 547 break; 548 case SIOCSLIFPHYRTABLE: 549 if ((error = suser(prc, 0)) != 0) 550 break; 551 if (ifr->ifr_rdomainid < 0 || 552 ifr->ifr_rdomainid > RT_TABLEID_MAX || 553 !rtable_exists(ifr->ifr_rdomainid)) { 554 error = EINVAL; 555 break; 556 } 557 sc->g_rtableid = ifr->ifr_rdomainid; 558 goto recompute; 559 case SIOCGLIFPHYRTABLE: 560 ifr->ifr_rdomainid = sc->g_rtableid; 561 break; 562 default: 563 error = ENOTTY; 564 } 565 566 splx(s); 567 return (error); 568 } 569 570 /* 571 * do a checksum of a buffer - much like in_cksum, which operates on 572 * mbufs. 573 */ 574 u_int16_t 575 gre_in_cksum(u_int16_t *p, u_int len) 576 { 577 u_int32_t sum = 0; 578 int nwords = len >> 1; 579 580 while (nwords-- != 0) 581 sum += *p++; 582 583 if (len & 1) { 584 union { 585 u_short w; 586 u_char c[2]; 587 } u; 588 u.c[0] = *(u_char *) p; 589 u.c[1] = 0; 590 sum += u.w; 591 } 592 593 /* end-around-carry */ 594 sum = (sum >> 16) + (sum & 0xffff); 595 sum += (sum >> 16); 596 return (~sum); 597 } 598 599 void 600 gre_keepalive(void *arg) 601 { 602 struct gre_softc *sc = arg; 603 604 if (!sc->sc_ka_timout) 605 return; 606 607 sc->sc_ka_state = GRE_STATE_DOWN; 608 gre_link_state(sc); 609 } 610 611 void 612 gre_send_keepalive(void *arg) 613 { 614 struct gre_softc *sc = arg; 615 struct mbuf *m; 616 struct ip *ip; 617 struct gre_h *gh; 618 struct sockaddr dst; 619 int s; 620 621 if (sc->sc_ka_timout) 622 timeout_add_sec(&sc->sc_ka_snd, sc->sc_ka_timout); 623 624 if (sc->g_proto != IPPROTO_GRE) 625 return; 626 if ((sc->sc_if.if_flags & IFF_UP) == 0 || 627 sc->g_src.s_addr == INADDR_ANY || sc->g_dst.s_addr == INADDR_ANY) 628 return; 629 630 MGETHDR(m, M_DONTWAIT, MT_DATA); 631 if (m == NULL) { 632 sc->sc_if.if_oerrors++; 633 return; 634 } 635 636 m->m_len = m->m_pkthdr.len = sizeof(*ip) + sizeof(*gh); 637 MH_ALIGN(m, m->m_len); 638 639 /* use the interface's rdomain when sending keepalives. */ 640 m->m_pkthdr.ph_rtableid = sc->sc_if.if_rdomain; 641 642 /* build the ip header */ 643 ip = mtod(m, struct ip *); 644 645 ip->ip_v = IPVERSION; 646 ip->ip_hl = sizeof(*ip) >> 2; 647 ip->ip_tos = IPTOS_LOWDELAY; 648 ip->ip_len = htons(m->m_pkthdr.len); 649 ip->ip_id = htons(ip_randomid()); 650 ip->ip_off = htons(IP_DF); 651 ip->ip_ttl = ip_defttl; 652 ip->ip_p = IPPROTO_GRE; 653 ip->ip_src.s_addr = sc->g_dst.s_addr; 654 ip->ip_dst.s_addr = sc->g_src.s_addr; 655 ip->ip_sum = 0; 656 ip->ip_sum = in_cksum(m, sizeof(*ip)); 657 658 gh = (struct gre_h *)(ip + 1); 659 /* We don't support any GRE flags for now */ 660 bzero(gh, sizeof(*gh)); 661 662 bzero(&dst, sizeof(dst)); 663 dst.sa_family = AF_INET; 664 665 s = splsoftnet(); 666 /* should we care about the error? */ 667 gre_output(&sc->sc_if, m, &dst, NULL); 668 splx(s); 669 } 670 671 void 672 gre_recv_keepalive(struct gre_softc *sc) 673 { 674 if (!sc->sc_ka_timout) 675 return; 676 677 /* link state flap dampening */ 678 switch (sc->sc_ka_state) { 679 case GRE_STATE_UKNWN: 680 case GRE_STATE_DOWN: 681 sc->sc_ka_state = GRE_STATE_HOLD; 682 sc->sc_ka_holdcnt = sc->sc_ka_holdmax; 683 sc->sc_ka_holdmax = MIN(sc->sc_ka_holdmax * 2, 684 16 * sc->sc_ka_cnt); 685 break; 686 case GRE_STATE_HOLD: 687 if (--sc->sc_ka_holdcnt < 1) { 688 sc->sc_ka_state = GRE_STATE_UP; 689 gre_link_state(sc); 690 } 691 break; 692 case GRE_STATE_UP: 693 sc->sc_ka_holdmax--; 694 sc->sc_ka_holdmax = MAX(sc->sc_ka_holdmax, sc->sc_ka_cnt); 695 break; 696 } 697 698 /* rescedule hold timer */ 699 timeout_add_sec(&sc->sc_ka_hold, sc->sc_ka_timout * sc->sc_ka_cnt); 700 } 701 702 void 703 gre_link_state(struct gre_softc *sc) 704 { 705 struct ifnet *ifp = &sc->sc_if; 706 int link_state = LINK_STATE_UNKNOWN; 707 708 if (sc->sc_ka_state == GRE_STATE_UP) 709 link_state = LINK_STATE_UP; 710 else if (sc->sc_ka_state != GRE_STATE_UKNWN) 711 link_state = LINK_STATE_KALIVE_DOWN; 712 713 if (ifp->if_link_state != link_state) { 714 ifp->if_link_state = link_state; 715 if_link_state_change(ifp); 716 } 717 } 718 #endif 719