1 /* $OpenBSD: if_gre.c,v 1.69 2014/07/12 18:44:22 tedu 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/netisr.h> 58 #include <net/route.h> 59 60 #ifdef INET 61 #include <netinet/in.h> 62 #include <netinet/in_systm.h> 63 #include <netinet/ip.h> 64 #include <netinet/ip_var.h> 65 #include <netinet/if_ether.h> 66 #else 67 #error "if_gre used without inet" 68 #endif 69 70 #if NBPFILTER > 0 71 #include <net/bpf.h> 72 #endif 73 74 #if NPF > 0 75 #include <net/pfvar.h> 76 #endif 77 78 #include <net/if_gre.h> 79 80 #ifndef GRE_RECURSION_LIMIT 81 #define GRE_RECURSION_LIMIT 3 /* How many levels of recursion allowed */ 82 #endif /* GRE_RECURSION_LIMIT */ 83 84 /* 85 * It is not easy to calculate the right value for a GRE MTU. 86 * We leave this task to the admin and use the same default that 87 * other vendors use. 88 */ 89 #define GREMTU 1476 90 91 int gre_clone_create(struct if_clone *, int); 92 int gre_clone_destroy(struct ifnet *); 93 94 struct gre_softc_head gre_softc_list; 95 struct if_clone gre_cloner = 96 IF_CLONE_INITIALIZER("gre", gre_clone_create, gre_clone_destroy); 97 98 /* 99 * We can control the acceptance of GRE and MobileIP packets by 100 * altering the sysctl net.inet.gre.allow and net.inet.mobileip.allow values 101 * respectively. Zero means drop them, all else is acceptance. We can also 102 * control acceptance of WCCPv1-style GRE packets through the 103 * net.inet.gre.wccp value, but be aware it depends upon normal GRE being 104 * allowed as well. 105 * 106 */ 107 int gre_allow = 0; 108 int gre_wccp = 0; 109 int ip_mobile_allow = 0; 110 111 void gre_keepalive(void *); 112 void gre_send_keepalive(void *); 113 void gre_link_state(struct gre_softc *); 114 115 void 116 greattach(int n) 117 { 118 LIST_INIT(&gre_softc_list); 119 if_clone_attach(&gre_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_collisions = 0; 142 sc->sc_if.if_ierrors = 0; 143 sc->sc_if.if_oerrors = 0; 144 sc->sc_if.if_ipackets = 0; 145 sc->sc_if.if_opackets = 0; 146 sc->g_dst.s_addr = sc->g_src.s_addr = INADDR_ANY; 147 sc->g_proto = IPPROTO_GRE; 148 sc->sc_if.if_flags |= IFF_LINK0; 149 sc->sc_ka_state = GRE_STATE_UKNWN; 150 151 timeout_set(&sc->sc_ka_hold, gre_keepalive, sc); 152 timeout_set(&sc->sc_ka_snd, gre_send_keepalive, sc); 153 154 if_attach(&sc->sc_if); 155 if_alloc_sadl(&sc->sc_if); 156 157 #if NBPFILTER > 0 158 bpfattach(&sc->sc_if.if_bpf, &sc->sc_if, DLT_LOOP, sizeof(u_int32_t)); 159 #endif 160 s = splnet(); 161 LIST_INSERT_HEAD(&gre_softc_list, sc, sc_list); 162 splx(s); 163 164 return (0); 165 } 166 167 int 168 gre_clone_destroy(struct ifnet *ifp) 169 { 170 struct gre_softc *sc = ifp->if_softc; 171 int s; 172 173 s = splnet(); 174 timeout_del(&sc->sc_ka_snd); 175 timeout_del(&sc->sc_ka_hold); 176 LIST_REMOVE(sc, sc_list); 177 splx(s); 178 179 if_detach(ifp); 180 181 free(sc, M_DEVBUF, 0); 182 return (0); 183 } 184 185 /* 186 * The output routine. Takes a packet and encapsulates it in the protocol 187 * given by sc->g_proto. See also RFC 1701 and RFC 2004. 188 */ 189 190 int 191 gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 192 struct rtentry *rt) 193 { 194 int error = 0; 195 struct gre_softc *sc = (struct gre_softc *) (ifp->if_softc); 196 struct greip *gh = NULL; 197 struct ip *inp = NULL; 198 u_int8_t ip_tos = 0; 199 u_int16_t etype = 0; 200 struct mobile_h mob_h; 201 struct m_tag *mtag; 202 203 if ((ifp->if_flags & IFF_UP) == 0 || 204 sc->g_src.s_addr == INADDR_ANY || sc->g_dst.s_addr == INADDR_ANY) { 205 m_freem(m); 206 error = ENETDOWN; 207 goto end; 208 } 209 210 #ifdef DIAGNOSTIC 211 if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid)) { 212 printf("%s: trying to send packet on wrong domain. " 213 "if %d vs. mbuf %d, AF %d\n", ifp->if_xname, 214 ifp->if_rdomain, rtable_l2(m->m_pkthdr.ph_rtableid), 215 dst->sa_family); 216 } 217 #endif 218 219 /* Try to limit infinite recursion through misconfiguration. */ 220 for (mtag = m_tag_find(m, PACKET_TAG_GRE, NULL); mtag; 221 mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) { 222 if (!bcmp((caddr_t)(mtag + 1), &ifp, sizeof(struct ifnet *))) { 223 IF_DROP(&ifp->if_snd); 224 m_freem(m); 225 error = EIO; 226 goto end; 227 } 228 } 229 230 mtag = m_tag_get(PACKET_TAG_GRE, sizeof(struct ifnet *), M_NOWAIT); 231 if (mtag == NULL) { 232 IF_DROP(&ifp->if_snd); 233 m_freem(m); 234 error = ENOBUFS; 235 goto end; 236 } 237 bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *)); 238 m_tag_prepend(m, mtag); 239 240 m->m_flags &= ~(M_BCAST|M_MCAST); 241 242 #if NBPFILTER > 0 243 if (ifp->if_bpf) 244 bpf_mtap_af(ifp->if_bpf, dst->sa_family, m, BPF_DIRECTION_OUT); 245 #endif 246 247 if (sc->g_proto == IPPROTO_MOBILE) { 248 if (ip_mobile_allow == 0) { 249 IF_DROP(&ifp->if_snd); 250 m_freem(m); 251 error = EACCES; 252 goto end; 253 } 254 255 if (dst->sa_family == AF_INET) { 256 struct mbuf *m0; 257 int msiz; 258 259 /* 260 * Make sure the complete IP header (with options) 261 * is in the first mbuf. 262 */ 263 if (m->m_len < sizeof(struct ip)) { 264 m = m_pullup(m, sizeof(struct ip)); 265 if (m == NULL) { 266 IF_DROP(&ifp->if_snd); 267 error = ENOBUFS; 268 goto end; 269 } else 270 inp = mtod(m, struct ip *); 271 272 if (m->m_len < inp->ip_hl << 2) { 273 m = m_pullup(m, inp->ip_hl << 2); 274 if (m == NULL) { 275 IF_DROP(&ifp->if_snd); 276 error = ENOBUFS; 277 goto end; 278 } 279 } 280 } 281 282 inp = mtod(m, struct ip *); 283 284 bzero(&mob_h, MOB_H_SIZ_L); 285 mob_h.proto = (inp->ip_p) << 8; 286 mob_h.odst = inp->ip_dst.s_addr; 287 inp->ip_dst.s_addr = sc->g_dst.s_addr; 288 289 /* 290 * If the packet comes from our host, we only change 291 * the destination address in the IP header. 292 * Otherwise we need to save and change the source. 293 */ 294 if (inp->ip_src.s_addr == sc->g_src.s_addr) { 295 msiz = MOB_H_SIZ_S; 296 } else { 297 mob_h.proto |= MOB_H_SBIT; 298 mob_h.osrc = inp->ip_src.s_addr; 299 inp->ip_src.s_addr = sc->g_src.s_addr; 300 msiz = MOB_H_SIZ_L; 301 } 302 303 HTONS(mob_h.proto); 304 mob_h.hcrc = gre_in_cksum((u_int16_t *) &mob_h, msiz); 305 306 /* Squeeze in the mobility header */ 307 if ((m->m_data - msiz) < m->m_pktdat) { 308 /* Need new mbuf */ 309 MGETHDR(m0, M_DONTWAIT, MT_HEADER); 310 if (m0 == NULL) { 311 IF_DROP(&ifp->if_snd); 312 m_freem(m); 313 error = ENOBUFS; 314 goto end; 315 } 316 M_MOVE_HDR(m0, m); 317 318 m0->m_len = msiz + (inp->ip_hl << 2); 319 m0->m_data += max_linkhdr; 320 m0->m_pkthdr.len = m->m_pkthdr.len + msiz; 321 m->m_data += inp->ip_hl << 2; 322 m->m_len -= inp->ip_hl << 2; 323 324 bcopy((caddr_t) inp, mtod(m0, caddr_t), 325 sizeof(struct ip)); 326 327 m0->m_next = m; 328 m = m0; 329 } else { /* we have some space left in the old one */ 330 m->m_data -= msiz; 331 m->m_len += msiz; 332 m->m_pkthdr.len += msiz; 333 bcopy(inp, mtod(m, caddr_t), 334 inp->ip_hl << 2); 335 } 336 337 /* Copy Mobility header */ 338 inp = mtod(m, struct ip *); 339 bcopy(&mob_h, (caddr_t)(inp + 1), (unsigned) msiz); 340 inp->ip_len = htons(ntohs(inp->ip_len) + msiz); 341 } else { /* AF_INET */ 342 IF_DROP(&ifp->if_snd); 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 IF_DROP(&ifp->if_snd); 350 m_freem(m); 351 error = EACCES; 352 goto end; 353 } 354 355 switch(dst->sa_family) { 356 case AF_INET: 357 if (m->m_len < sizeof(struct ip)) { 358 m = m_pullup(m, sizeof(struct ip)); 359 if (m == NULL) { 360 IF_DROP(&ifp->if_snd); 361 error = ENOBUFS; 362 goto end; 363 } 364 } 365 366 inp = mtod(m, struct ip *); 367 ip_tos = inp->ip_tos; 368 etype = ETHERTYPE_IP; 369 break; 370 #ifdef INET6 371 case AF_INET6: 372 etype = ETHERTYPE_IPV6; 373 break; 374 #endif 375 #ifdef MPLS 376 case AF_MPLS: 377 if (m->m_flags & (M_BCAST | M_MCAST)) 378 etype = ETHERTYPE_MPLS_MCAST; 379 else 380 etype = ETHERTYPE_MPLS; 381 break; 382 #endif 383 default: 384 IF_DROP(&ifp->if_snd); 385 m_freem(m); 386 error = EAFNOSUPPORT; 387 goto end; 388 } 389 390 M_PREPEND(m, sizeof(struct greip), M_DONTWAIT); 391 } else { 392 IF_DROP(&ifp->if_snd); 393 m_freem(m); 394 error = EINVAL; 395 goto end; 396 } 397 398 if (m == NULL) { 399 IF_DROP(&ifp->if_snd); 400 error = ENOBUFS; 401 goto end; 402 } 403 404 gh = mtod(m, struct greip *); 405 if (sc->g_proto == IPPROTO_GRE) { 406 /* We don't support any GRE flags for now */ 407 408 bzero((void *) &gh->gi_g, sizeof(struct gre_h)); 409 gh->gi_ptype = htons(etype); 410 } 411 412 gh->gi_pr = sc->g_proto; 413 if (sc->g_proto != IPPROTO_MOBILE) { 414 gh->gi_src = sc->g_src; 415 gh->gi_dst = sc->g_dst; 416 ((struct ip *) gh)->ip_hl = (sizeof(struct ip)) >> 2; 417 ((struct ip *) gh)->ip_ttl = ip_defttl; 418 ((struct ip *) gh)->ip_tos = ip_tos; 419 gh->gi_len = htons(m->m_pkthdr.len); 420 } 421 422 ifp->if_opackets++; 423 ifp->if_obytes += m->m_pkthdr.len; 424 425 426 m->m_pkthdr.ph_rtableid = sc->g_rtableid; 427 428 #if NPF > 0 429 pf_pkt_addr_changed(m); 430 #endif 431 432 /* Send it off */ 433 error = ip_output(m, NULL, &sc->route, 0, NULL, NULL, 0); 434 end: 435 if (error) 436 ifp->if_oerrors++; 437 return (error); 438 } 439 440 int 441 gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 442 { 443 444 struct ifreq *ifr = (struct ifreq *)data; 445 struct ifaddr *ifa = (struct ifaddr *)data; 446 struct if_laddrreq *lifr = (struct if_laddrreq *)data; 447 struct ifkalivereq *ikar = (struct ifkalivereq *)data; 448 struct gre_softc *sc = ifp->if_softc; 449 int s; 450 struct sockaddr_in si; 451 struct sockaddr *sa = NULL; 452 int error = 0; 453 struct proc *prc = curproc; /* XXX */ 454 455 s = splnet(); 456 switch(cmd) { 457 case SIOCSIFADDR: 458 ifp->if_flags |= IFF_UP; 459 ifa->ifa_rtrequest = p2p_rtrequest; 460 break; 461 case SIOCSIFDSTADDR: 462 break; 463 case SIOCSIFFLAGS: 464 if ((ifr->ifr_flags & IFF_LINK0) != 0) 465 sc->g_proto = IPPROTO_GRE; 466 else 467 sc->g_proto = IPPROTO_MOBILE; 468 break; 469 case SIOCSIFMTU: 470 if (ifr->ifr_mtu < 576) { 471 error = EINVAL; 472 break; 473 } 474 ifp->if_mtu = ifr->ifr_mtu; 475 break; 476 case SIOCGIFMTU: 477 ifr->ifr_mtu = sc->sc_if.if_mtu; 478 break; 479 case SIOCGIFHARDMTU: 480 ifr->ifr_hardmtu = sc->sc_if.if_hardmtu; 481 break; 482 case SIOCADDMULTI: 483 case SIOCDELMULTI: 484 break; 485 case GRESPROTO: 486 /* Check for superuser */ 487 if ((error = suser(prc, 0)) != 0) 488 break; 489 490 sc->g_proto = ifr->ifr_flags; 491 switch (sc->g_proto) { 492 case IPPROTO_GRE: 493 ifp->if_flags |= IFF_LINK0; 494 break; 495 case IPPROTO_MOBILE: 496 ifp->if_flags &= ~IFF_LINK0; 497 break; 498 default: 499 error = EPROTONOSUPPORT; 500 break; 501 } 502 break; 503 case GREGPROTO: 504 ifr->ifr_flags = sc->g_proto; 505 break; 506 case GRESADDRS: 507 case GRESADDRD: 508 /* Check for superuser */ 509 if ((error = suser(prc, 0)) != 0) 510 break; 511 512 /* 513 * set tunnel endpoints and mark if as up 514 */ 515 sa = &ifr->ifr_addr; 516 if (cmd == GRESADDRS ) 517 sc->g_src = (satosin(sa))->sin_addr; 518 if (cmd == GRESADDRD ) 519 sc->g_dst = (satosin(sa))->sin_addr; 520 recompute: 521 if ((sc->g_src.s_addr != INADDR_ANY) && 522 (sc->g_dst.s_addr != INADDR_ANY)) { 523 if (sc->route.ro_rt != 0) 524 RTFREE(sc->route.ro_rt); 525 /* ip_output() will do the lookup */ 526 bzero(&sc->route, sizeof(sc->route)); 527 ifp->if_flags |= IFF_UP; 528 } 529 break; 530 case GREGADDRS: 531 bzero(&si, sizeof(si)); 532 si.sin_family = AF_INET; 533 si.sin_len = sizeof(struct sockaddr_in); 534 si.sin_addr.s_addr = sc->g_src.s_addr; 535 sa = sintosa(&si); 536 ifr->ifr_addr = *sa; 537 break; 538 case GREGADDRD: 539 bzero(&si, sizeof(si)); 540 si.sin_family = AF_INET; 541 si.sin_len = sizeof(struct sockaddr_in); 542 si.sin_addr.s_addr = sc->g_dst.s_addr; 543 sa = sintosa(&si); 544 ifr->ifr_addr = *sa; 545 break; 546 case SIOCSETKALIVE: 547 if ((error = suser(prc, 0)) != 0) 548 break; 549 if (ikar->ikar_timeo < 0 || ikar->ikar_timeo > 86400 || 550 ikar->ikar_cnt < 0 || ikar->ikar_cnt > 256) { 551 error = EINVAL; 552 break; 553 } 554 sc->sc_ka_timout = ikar->ikar_timeo; 555 sc->sc_ka_cnt = ikar->ikar_cnt; 556 if (sc->sc_ka_timout == 0 || sc->sc_ka_cnt == 0) { 557 sc->sc_ka_timout = 0; 558 sc->sc_ka_cnt = 0; 559 sc->sc_ka_state = GRE_STATE_UKNWN; 560 gre_link_state(sc); 561 break; 562 } 563 if (!timeout_pending(&sc->sc_ka_snd)) { 564 sc->sc_ka_holdmax = sc->sc_ka_cnt; 565 timeout_add(&sc->sc_ka_snd, 1); 566 timeout_add_sec(&sc->sc_ka_hold, sc->sc_ka_timout * 567 sc->sc_ka_cnt); 568 } 569 break; 570 case SIOCGETKALIVE: 571 ikar->ikar_timeo = sc->sc_ka_timout; 572 ikar->ikar_cnt = sc->sc_ka_cnt; 573 break; 574 case SIOCSLIFPHYADDR: 575 if ((error = suser(prc, 0)) != 0) 576 break; 577 if (lifr->addr.ss_family != AF_INET || 578 lifr->dstaddr.ss_family != AF_INET) { 579 error = EAFNOSUPPORT; 580 break; 581 } 582 if (lifr->addr.ss_len != sizeof(si) || 583 lifr->dstaddr.ss_len != sizeof(si)) { 584 error = EINVAL; 585 break; 586 } 587 sc->g_src = ((struct sockaddr_in *)&lifr->addr)->sin_addr; 588 sc->g_dst = ((struct sockaddr_in *)&lifr->dstaddr)->sin_addr; 589 goto recompute; 590 case SIOCDIFPHYADDR: 591 if ((error = suser(prc, 0)) != 0) 592 break; 593 sc->g_src.s_addr = INADDR_ANY; 594 sc->g_dst.s_addr = INADDR_ANY; 595 break; 596 case SIOCGLIFPHYADDR: 597 if (sc->g_src.s_addr == INADDR_ANY || 598 sc->g_dst.s_addr == INADDR_ANY) { 599 error = EADDRNOTAVAIL; 600 break; 601 } 602 bzero(&si, sizeof(si)); 603 si.sin_family = AF_INET; 604 si.sin_len = sizeof(struct sockaddr_in); 605 si.sin_addr.s_addr = sc->g_src.s_addr; 606 memcpy(&lifr->addr, &si, sizeof(si)); 607 si.sin_addr.s_addr = sc->g_dst.s_addr; 608 memcpy(&lifr->dstaddr, &si, sizeof(si)); 609 break; 610 case SIOCSLIFPHYRTABLE: 611 if ((error = suser(prc, 0)) != 0) 612 break; 613 if (ifr->ifr_rdomainid < 0 || 614 ifr->ifr_rdomainid > RT_TABLEID_MAX || 615 !rtable_exists(ifr->ifr_rdomainid)) { 616 error = EINVAL; 617 break; 618 } 619 sc->g_rtableid = ifr->ifr_rdomainid; 620 goto recompute; 621 case SIOCGLIFPHYRTABLE: 622 ifr->ifr_rdomainid = sc->g_rtableid; 623 break; 624 default: 625 error = ENOTTY; 626 } 627 628 splx(s); 629 return (error); 630 } 631 632 /* 633 * do a checksum of a buffer - much like in_cksum, which operates on 634 * mbufs. 635 */ 636 u_int16_t 637 gre_in_cksum(u_int16_t *p, u_int len) 638 { 639 u_int32_t sum = 0; 640 int nwords = len >> 1; 641 642 while (nwords-- != 0) 643 sum += *p++; 644 645 if (len & 1) { 646 union { 647 u_short w; 648 u_char c[2]; 649 } u; 650 u.c[0] = *(u_char *) p; 651 u.c[1] = 0; 652 sum += u.w; 653 } 654 655 /* end-around-carry */ 656 sum = (sum >> 16) + (sum & 0xffff); 657 sum += (sum >> 16); 658 return (~sum); 659 } 660 661 void 662 gre_keepalive(void *arg) 663 { 664 struct gre_softc *sc = arg; 665 666 if (!sc->sc_ka_timout) 667 return; 668 669 sc->sc_ka_state = GRE_STATE_DOWN; 670 gre_link_state(sc); 671 } 672 673 void 674 gre_send_keepalive(void *arg) 675 { 676 struct gre_softc *sc = arg; 677 struct mbuf *m; 678 struct ip *ip; 679 struct gre_h *gh; 680 struct sockaddr dst; 681 int s; 682 683 if (sc->sc_ka_timout) 684 timeout_add_sec(&sc->sc_ka_snd, sc->sc_ka_timout); 685 686 if (sc->g_proto != IPPROTO_GRE) 687 return; 688 if ((sc->sc_if.if_flags & IFF_UP) == 0 || 689 sc->g_src.s_addr == INADDR_ANY || sc->g_dst.s_addr == INADDR_ANY) 690 return; 691 692 MGETHDR(m, M_DONTWAIT, MT_DATA); 693 if (m == NULL) { 694 sc->sc_if.if_oerrors++; 695 return; 696 } 697 698 m->m_len = m->m_pkthdr.len = sizeof(*ip) + sizeof(*gh); 699 MH_ALIGN(m, m->m_len); 700 701 /* build the ip header */ 702 ip = mtod(m, struct ip *); 703 704 ip->ip_v = IPVERSION; 705 ip->ip_hl = sizeof(*ip) >> 2; 706 ip->ip_tos = IPTOS_LOWDELAY; 707 ip->ip_len = htons(m->m_pkthdr.len); 708 ip->ip_id = htons(ip_randomid()); 709 ip->ip_off = htons(IP_DF); 710 ip->ip_ttl = ip_defttl; 711 ip->ip_p = IPPROTO_GRE; 712 ip->ip_src.s_addr = sc->g_dst.s_addr; 713 ip->ip_dst.s_addr = sc->g_src.s_addr; 714 ip->ip_sum = 0; 715 ip->ip_sum = in_cksum(m, sizeof(*ip)); 716 717 gh = (struct gre_h *)(ip + 1); 718 /* We don't support any GRE flags for now */ 719 bzero(gh, sizeof(*gh)); 720 721 bzero(&dst, sizeof(dst)); 722 dst.sa_family = AF_INET; 723 724 s = splsoftnet(); 725 /* should we care about the error? */ 726 gre_output(&sc->sc_if, m, &dst, NULL); 727 splx(s); 728 } 729 730 void 731 gre_recv_keepalive(struct gre_softc *sc) 732 { 733 if (!sc->sc_ka_timout) 734 return; 735 736 /* link state flap dampening */ 737 switch (sc->sc_ka_state) { 738 case GRE_STATE_UKNWN: 739 case GRE_STATE_DOWN: 740 sc->sc_ka_state = GRE_STATE_HOLD; 741 sc->sc_ka_holdcnt = sc->sc_ka_holdmax; 742 sc->sc_ka_holdmax = MIN(sc->sc_ka_holdmax * 2, 743 16 * sc->sc_ka_cnt); 744 break; 745 case GRE_STATE_HOLD: 746 if (--sc->sc_ka_holdcnt < 1) { 747 sc->sc_ka_state = GRE_STATE_UP; 748 gre_link_state(sc); 749 } 750 break; 751 case GRE_STATE_UP: 752 sc->sc_ka_holdmax--; 753 sc->sc_ka_holdmax = MAX(sc->sc_ka_holdmax, sc->sc_ka_cnt); 754 break; 755 } 756 757 /* rescedule hold timer */ 758 timeout_add_sec(&sc->sc_ka_hold, sc->sc_ka_timout * sc->sc_ka_cnt); 759 } 760 761 void 762 gre_link_state(struct gre_softc *sc) 763 { 764 struct ifnet *ifp = &sc->sc_if; 765 int link_state = LINK_STATE_UNKNOWN; 766 767 if (sc->sc_ka_state == GRE_STATE_UP) 768 link_state = LINK_STATE_UP; 769 else if (sc->sc_ka_state != GRE_STATE_UKNWN) 770 link_state = LINK_STATE_KALIVE_DOWN; 771 772 if (ifp->if_link_state != link_state) { 773 ifp->if_link_state = link_state; 774 if_link_state_change(ifp); 775 } 776 } 777 #endif 778