summaryrefslogtreecommitdiff
path: root/sys/net/if_gre.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net/if_gre.c')
-rw-r--r--sys/net/if_gre.c160
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