xref: /openbsd-src/sys/net/if_gre.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
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