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