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