diff options
Diffstat (limited to 'sys/net/if_gre.c')
-rw-r--r-- | sys/net/if_gre.c | 160 |
1 files changed, 156 insertions, 4 deletions
diff --git a/sys/net/if_gre.c b/sys/net/if_gre.c index fde4de2c306..3db43e3e2c1 100644 --- a/sys/net/if_gre.c +++ b/sys/net/if_gre.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_gre.c,v 1.48 2010/05/11 09:22:56 claudio Exp $ */ +/* $OpenBSD: if_gre.c,v 1.49 2010/06/26 19:49:54 claudio Exp $ */ /* $NetBSD: if_gre.c,v 1.9 1999/10/25 19:18:11 drochner Exp $ */ /* @@ -115,7 +115,10 @@ int gre_allow = 0; int gre_wccp = 0; int ip_mobile_allow = 0; -static void gre_compute_route(struct gre_softc *sc); +void gre_compute_route(struct gre_softc *); +void gre_keepalive(void *); +void gre_send_keepalive(void *); +void gre_link_state(struct gre_softc *); void greattach(int n) @@ -151,6 +154,10 @@ gre_clone_create(struct if_clone *ifc, int unit) sc->g_dst.s_addr = sc->g_src.s_addr = INADDR_ANY; sc->g_proto = IPPROTO_GRE; sc->sc_if.if_flags |= IFF_LINK0; + sc->sc_ka_state = GRE_STATE_UKNWN; + + timeout_set(&sc->sc_ka_hold, gre_keepalive, sc); + timeout_set(&sc->sc_ka_snd, gre_send_keepalive, sc); if_attach(&sc->sc_if); if_alloc_sadl(&sc->sc_if); @@ -239,7 +246,7 @@ gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, m->m_flags &= ~(M_BCAST|M_MCAST); -#if NBPFILTER >0 +#if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap_af(ifp->if_bpf, dst->sa_family, m, BPF_DIRECTION_OUT); #endif @@ -448,6 +455,7 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct ifreq *ifr = (struct ifreq *) data; struct if_laddrreq *lifr = (struct if_laddrreq *)data; + struct ifkalivereq *ikar = (struct ifkalivereq *)data; struct gre_softc *sc = ifp->if_softc; int s; struct sockaddr_in si; @@ -562,6 +570,34 @@ recompute: sa = sintosa(&si); ifr->ifr_addr = *sa; break; + case SIOCSETKALIVE: + if ((error = suser(prc, 0)) != 0) + break; + if (ikar->ikar_timeo < 0 || ikar->ikar_timeo > 86400 || + ikar->ikar_cnt < 0 || ikar->ikar_cnt > 256) { + error = EINVAL; + break; + } + sc->sc_ka_timout = ikar->ikar_timeo; + sc->sc_ka_cnt = ikar->ikar_cnt; + if (sc->sc_ka_timout == 0 || sc->sc_ka_cnt == 0) { + sc->sc_ka_timout = 0; + sc->sc_ka_cnt = 0; + sc->sc_ka_state = GRE_STATE_UKNWN; + gre_link_state(sc); + break; + } + if (!timeout_pending(&sc->sc_ka_snd)) { + sc->sc_ka_holdmax = sc->sc_ka_cnt; + timeout_add(&sc->sc_ka_snd, 1); + timeout_add_sec(&sc->sc_ka_hold, sc->sc_ka_timout * + sc->sc_ka_cnt); + } + break; + case SIOCGETKALIVE: + ikar->ikar_timeo = sc->sc_ka_timout; + ikar->ikar_cnt = sc->sc_ka_cnt; + break; case SIOCSLIFPHYADDR: if ((error = suser(prc, 0)) != 0) break; @@ -633,7 +669,7 @@ recompute: * at least a default route which matches. */ -static void +void gre_compute_route(struct gre_softc *sc) { struct route *ro; @@ -713,4 +749,120 @@ gre_in_cksum(u_int16_t *p, u_int len) sum += (sum >> 16); return (~sum); } + +void +gre_keepalive(void *arg) +{ + struct gre_softc *sc = arg; + + if (!sc->sc_ka_timout) + return; + + sc->sc_ka_state = GRE_STATE_DOWN; + gre_link_state(sc); +} + +void +gre_send_keepalive(void *arg) +{ + struct gre_softc *sc = arg; + struct mbuf *m; + struct ip *ip; + struct gre_h *gh; + struct sockaddr dst; + int s; + + if (sc->sc_ka_timout) + timeout_add_sec(&sc->sc_ka_snd, sc->sc_ka_timout); + + if (sc->g_proto != IPPROTO_GRE) + return; + if ((sc->sc_if.if_flags & IFF_UP) == 0 || + sc->g_src.s_addr == INADDR_ANY || sc->g_dst.s_addr == INADDR_ANY) + return; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + sc->sc_if.if_oerrors++; + return; + } + + m->m_len = m->m_pkthdr.len = sizeof(*ip) + sizeof(*gh); + MH_ALIGN(m, m->m_len); + + /* build the ip header */ + ip = (struct ip *)m->m_data; + + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_tos = IPTOS_LOWDELAY; + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_id = htons(ip_randomid()); + ip->ip_off = htons(IP_DF); + ip->ip_ttl = ip_defttl; + ip->ip_p = IPPROTO_GRE; + ip->ip_src.s_addr = sc->g_dst.s_addr; + ip->ip_dst.s_addr = sc->g_src.s_addr; + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m, sizeof(*ip)); + + gh = (struct gre_h *)(ip + 1); + /* We don't support any GRE flags for now */ + bzero(gh, sizeof(*gh)); + + bzero(&dst, sizeof(dst)); + dst.sa_family = AF_INET; + + s = splsoftnet(); + /* should we care about the error? */ + gre_output(&sc->sc_if, m, &dst, NULL); + splx(s); +} + +void +gre_recv_keepalive(struct gre_softc *sc) +{ + if (!sc->sc_ka_timout) + return; + + /* link state flap dampening */ + switch (sc->sc_ka_state) { + case GRE_STATE_UKNWN: + case GRE_STATE_DOWN: + sc->sc_ka_state = GRE_STATE_HOLD; + sc->sc_ka_holdcnt = sc->sc_ka_holdmax; + sc->sc_ka_holdmax = MIN(sc->sc_ka_holdmax * 2, + 16 * sc->sc_ka_cnt); + break; + case GRE_STATE_HOLD: + if (--sc->sc_ka_holdcnt < 1) { + sc->sc_ka_state = GRE_STATE_UP; + gre_link_state(sc); + } + break; + case GRE_STATE_UP: + sc->sc_ka_holdmax = MAX(sc->sc_ka_holdmax--, sc->sc_ka_cnt); + break; + } + + /* rescedule hold timer */ + timeout_add_sec(&sc->sc_ka_hold, sc->sc_ka_timout * sc->sc_ka_cnt); +} + +void +gre_link_state(struct gre_softc *sc) +{ + struct ifnet *ifp = &sc->sc_if; + int link_state = LINK_STATE_UNKNOWN; + + if (sc->sc_ka_state == GRE_STATE_UP) + link_state = LINK_STATE_UP; + else if (sc->sc_ka_state != GRE_STATE_UKNWN) + link_state = LINK_STATE_KALIVE_DOWN; + + if (ifp->if_link_state != link_state) { + ifp->if_link_state = link_state; + if_link_state_change(ifp); + } +} #endif |