xref: /openbsd-src/sys/net/if_gre.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*      $OpenBSD: if_gre.c,v 1.28 2003/08/15 20:32:19 tedu 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  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *        This product includes software developed by the NetBSD
22  *        Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 /*
41  * Encapsulate L3 protocols into IP, per RFC 1701 and 1702.
42  * See gre(4) for more details.
43  * Also supported: IP in IP encapsulation (proto 55) per RFC 2004.
44  */
45 
46 #include "gre.h"
47 #if NGRE > 0
48 
49 #include "bpfilter.h"
50 
51 #include <sys/param.h>
52 #include <sys/proc.h>
53 #include <sys/mbuf.h>
54 #include <sys/socket.h>
55 #include <sys/sockio.h>
56 #include <sys/kernel.h>
57 #include <sys/systm.h>
58 
59 #include <net/if.h>
60 #include <net/if_types.h>
61 #include <net/netisr.h>
62 #include <net/route.h>
63 
64 #ifdef INET
65 #include <netinet/in.h>
66 #include <netinet/in_systm.h>
67 #include <netinet/in_var.h>
68 #include <netinet/ip.h>
69 #include <netinet/ip_var.h>
70 #include <netinet/if_ether.h>
71 #else
72 #error "if_gre used without inet"
73 #endif
74 
75 #ifdef NS
76 #include <netns/ns.h>
77 #include <netns/ns_if.h>
78 #endif
79 
80 #ifdef NETATALK
81 #include <netatalk/at.h>
82 #include <netatalk/at_var.h>
83 #include <netatalk/at_extern.h>
84 #endif
85 
86 #if NBPFILTER > 0
87 #include <net/bpf.h>
88 #endif
89 
90 #include <net/if_gre.h>
91 
92 #ifndef GRE_RECURSION_LIMIT
93 #define GRE_RECURSION_LIMIT	3   /* How many levels of recursion allowed */
94 #endif /* GRE_RECURSION_LIMIT */
95 
96 #define GREMTU 1450	/* XXX this is below the standard MTU of
97                          1500 Bytes, allowing for headers,
98                          but we should possibly do path mtu discovery
99                          before changing if state to up to find the
100                          correct value */
101 
102 struct gre_softc *gre = 0;
103 
104 int ngre = 0;
105 
106 /*
107  * We can control the acceptance of GRE and MobileIP packets by
108  * altering the sysctl net.inet.gre.allow and net.inet.mobileip.allow values
109  * respectively. Zero means drop them, all else is acceptance.  We can also
110  * control acceptance of WCCPv1-style GRE packets through the
111  * net.inet.gre.wccp value, but be aware it depends upon normal GRE being
112  * allowed as well.
113  *
114  */
115 int gre_allow = 0;
116 int gre_wccp = 0;
117 int ip_mobile_allow = 0;
118 
119 static void gre_compute_route(struct gre_softc *sc);
120 
121 void
122 greattach(n)
123 	int n;
124 {
125 	struct gre_softc *sc;
126 	int i;
127 
128 	ngre = n;
129 	gre = sc = malloc(ngre * sizeof(struct gre_softc), M_DEVBUF, M_WAIT);
130 	bzero(sc, ngre * sizeof(struct gre_softc));
131 	for (i = 0; i < ngre ; sc++) {
132 		snprintf(sc->sc_if.if_xname, sizeof(sc->sc_if.if_xname),
133 			 "gre%d", i++);
134 		sc->sc_if.if_softc = sc;
135 		sc->sc_if.if_type = IFT_OTHER;
136 		sc->sc_if.if_addrlen = 0;
137 		sc->sc_if.if_hdrlen = 24; /* IP + GRE */
138 		sc->sc_if.if_mtu = GREMTU;
139 		sc->sc_if.if_flags = IFF_POINTOPOINT|IFF_MULTICAST;
140 		sc->sc_if.if_output = gre_output;
141 		sc->sc_if.if_ioctl = gre_ioctl;
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->g_proto = IPPROTO_GRE;
149 		sc->sc_if.if_flags |= IFF_LINK0;
150 
151 		if_attach(&sc->sc_if);
152 		if_alloc_sadl(&sc->sc_if);
153 
154 #if NBPFILTER > 0
155 		bpfattach(&sc->sc_if.if_bpf, &sc->sc_if, DLT_RAW,
156 		    sizeof(u_int32_t));
157 #endif
158 	}
159 }
160 
161 /*
162  * The output routine. Takes a packet and encapsulates it in the protocol
163  * given by sc->g_proto. See also RFC 1701 and RFC 2004.
164  */
165 
166 int
167 gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
168 	   struct rtentry *rt)
169 {
170 	int error = 0;
171 	struct gre_softc *sc = (struct gre_softc *) (ifp->if_softc);
172 	struct greip *gh = NULL;
173 	struct ip *inp = NULL;
174 	u_short etype = 0;
175 	struct mobile_h mob_h;
176 	struct m_tag *mtag;
177 
178 	if ((ifp->if_flags & IFF_UP) == 0 ||
179 	    sc->g_src.s_addr == INADDR_ANY || sc->g_dst.s_addr == INADDR_ANY) {
180 		m_freem(m);
181 		error = ENETDOWN;
182 		goto end;
183 	}
184 
185 	/* Try to limit infinite recursion through misconfiguration. */
186 	for (mtag = m_tag_find(m, PACKET_TAG_GRE, NULL); mtag;
187 	     mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) {
188 		if (!bcmp((caddr_t)(mtag + 1), &ifp, sizeof(struct ifnet *))) {
189 			IF_DROP(&ifp->if_snd);
190 			m_freem(m);
191 			error = EIO;
192 			goto end;
193 		}
194 	}
195 
196 	mtag = m_tag_get(PACKET_TAG_GRE, sizeof(struct ifnet *), M_NOWAIT);
197 	if (mtag == NULL) {
198 		IF_DROP(&ifp->if_snd);
199 		m_freem(m);
200 		error = ENOBUFS;
201 		goto end;
202 	}
203 	bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *));
204 	m_tag_prepend(m, mtag);
205 
206 #if NBPFILTER >0
207 	if (ifp->if_bpf)
208 		bpf_mtap(ifp->if_bpf, m);
209 #endif
210 
211 	if (sc->g_proto == IPPROTO_MOBILE) {
212 		if (ip_mobile_allow == 0) {
213 			IF_DROP(&ifp->if_snd);
214 			m_freem(m);
215 			error = EACCES;
216 			goto end;
217 		}
218 
219 		if (dst->sa_family == AF_INET) {
220 			struct mbuf *m0;
221 			int msiz;
222 
223 			/*
224 			 * Make sure the complete IP header (with options)
225 			 * is in the first mbuf.
226 			 */
227 			if (m->m_len < sizeof(struct ip)) {
228 				m = m_pullup(m, sizeof(struct ip));
229 				if (m == NULL) {
230 					IF_DROP(&ifp->if_snd);
231 					error = ENOBUFS;
232 					goto end;
233 				} else
234 					inp = mtod(m, struct ip *);
235 
236 				if (m->m_len < inp->ip_hl << 2) {
237 					m = m_pullup(m,
238 					    sizeof(inp->ip_hl << 2));
239 					if (m == NULL) {
240 						IF_DROP(&ifp->if_snd);
241 						error = ENOBUFS;
242 						goto end;
243 					}
244 				}
245 			}
246 
247 			inp = mtod(m, struct ip *);
248 
249 			bzero(&mob_h, MOB_H_SIZ_L);
250 			mob_h.proto = (inp->ip_p) << 8;
251 			mob_h.odst = inp->ip_dst.s_addr;
252 			inp->ip_dst.s_addr = sc->g_dst.s_addr;
253 
254 			/*
255 			 * If the packet comes from our host, we only change
256 			 * the destination address in the IP header.
257 			 * Otherwise we need to save and change the source.
258 			 */
259 			if (inp->ip_src.s_addr == sc->g_src.s_addr) {
260 				msiz = MOB_H_SIZ_S;
261 			} else {
262 				mob_h.proto |= MOB_H_SBIT;
263 				mob_h.osrc = inp->ip_src.s_addr;
264 				inp->ip_src.s_addr = sc->g_src.s_addr;
265 				msiz = MOB_H_SIZ_L;
266 			}
267 
268 			HTONS(mob_h.proto);
269 			mob_h.hcrc = gre_in_cksum((u_short *) &mob_h, msiz);
270 
271 			/* Squeeze in the mobility header */
272 			if ((m->m_data - msiz) < m->m_pktdat) {
273 				/* Need new mbuf */
274 				MGETHDR(m0, M_DONTWAIT, MT_HEADER);
275 				if (m0 == NULL) {
276 					IF_DROP(&ifp->if_snd);
277 					m_freem(m);
278 					error = ENOBUFS;
279 					goto end;
280 				}
281 				M_MOVE_HDR(m0, m);
282 
283 				m0->m_len = msiz + (inp->ip_hl << 2);
284 				m0->m_data += max_linkhdr;
285 				m0->m_pkthdr.len = m->m_pkthdr.len + msiz;
286 				m->m_data += inp->ip_hl << 2;
287 				m->m_len -= inp->ip_hl << 2;
288 
289 				bcopy((caddr_t) inp, mtod(m0, caddr_t),
290 				    sizeof(struct ip));
291 
292 				m0->m_next = m;
293 				m = m0;
294 			} else {  /* we have some space left in the old one */
295 				m->m_data -= msiz;
296 				m->m_len += msiz;
297 				m->m_pkthdr.len += msiz;
298 				bcopy(inp, mtod(m, caddr_t),
299 				    inp->ip_hl << 2);
300 			}
301 
302 			/* Copy Mobility header */
303 			inp = mtod(m, struct ip *);
304 			bcopy(&mob_h, (caddr_t)(inp + 1), (unsigned) msiz);
305 			inp->ip_len = htons(ntohs(inp->ip_len) + msiz);
306 		} else {  /* AF_INET */
307 			IF_DROP(&ifp->if_snd);
308 			m_freem(m);
309 			error = EINVAL;
310 			goto end;
311 		}
312 	} else if (sc->g_proto == IPPROTO_GRE) {
313 		if (gre_allow == 0) {
314 			IF_DROP(&ifp->if_snd);
315 			m_freem(m);
316 			error = EACCES;
317 			goto end;
318 		}
319 
320 		switch(dst->sa_family) {
321 		case AF_INET:
322 			if (m->m_len < sizeof(struct ip)) {
323 				m = m_pullup(m, sizeof(struct ip));
324 				if (m == NULL) {
325 					IF_DROP(&ifp->if_snd);
326 					error = ENOBUFS;
327 					goto end;
328 				}
329 			}
330 
331 			inp = mtod(m, struct ip *);
332 			etype = ETHERTYPE_IP;
333 			break;
334 #ifdef NETATALK
335 		case AF_APPLETALK:
336 			etype = ETHERTYPE_AT;
337 			break;
338 #endif
339 #ifdef NS
340 		case AF_NS:
341 			etype = ETHERTYPE_NS;
342 			break;
343 #endif
344 		default:
345 			IF_DROP(&ifp->if_snd);
346 			m_freem(m);
347 			error = EAFNOSUPPORT;
348 			goto end;
349 		}
350 
351 		M_PREPEND(m, sizeof(struct greip), M_DONTWAIT);
352 	} else {
353 		IF_DROP(&ifp->if_snd);
354 		m_freem(m);
355 		error = EINVAL;
356 		goto end;
357 	}
358 
359 	if (m == NULL) {
360 		IF_DROP(&ifp->if_snd);
361 		error = ENOBUFS;
362 		goto end;
363 	}
364 
365 	gh = mtod(m, struct greip *);
366 	if (sc->g_proto == IPPROTO_GRE) {
367 		/* We don't support any GRE flags for now */
368 
369 		bzero((void *) &gh->gi_g, sizeof(struct gre_h));
370 		gh->gi_ptype = htons(etype);
371 	}
372 
373 	gh->gi_pr = sc->g_proto;
374 	if (sc->g_proto != IPPROTO_MOBILE) {
375 		gh->gi_src = sc->g_src;
376 		gh->gi_dst = sc->g_dst;
377 		((struct ip *) gh)->ip_hl = (sizeof(struct ip)) >> 2;
378 		((struct ip *) gh)->ip_ttl = ip_defttl;
379 		((struct ip *) gh)->ip_tos = inp->ip_tos;
380 		gh->gi_len = m->m_pkthdr.len;
381 	}
382 
383 	ifp->if_opackets++;
384 	ifp->if_obytes += m->m_pkthdr.len;
385 
386 	/* Send it off */
387 	error = ip_output(m, (void *)NULL, &sc->route, 0, (void *)NULL, (void *)NULL);
388   end:
389 	if (error)
390 		ifp->if_oerrors++;
391 	return (error);
392 }
393 
394 int
395 gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
396 {
397 
398 	struct ifreq *ifr = (struct ifreq *) data;
399 	struct if_laddrreq *lifr = (struct if_laddrreq *)data;
400 	struct gre_softc *sc = ifp->if_softc;
401 	int s;
402 	struct sockaddr_in si;
403 	struct sockaddr *sa = NULL;
404 	int error = 0;
405 	struct proc *prc = curproc;		/* XXX */
406 
407 	s = splimp();
408 	switch(cmd) {
409 	case SIOCSIFADDR:
410 		ifp->if_flags |= IFF_UP;
411 		break;
412 	case SIOCSIFDSTADDR:
413 		break;
414 	case SIOCSIFFLAGS:
415 		if ((ifr->ifr_flags & IFF_LINK0) != 0)
416 			sc->g_proto = IPPROTO_GRE;
417 		else
418 			sc->g_proto = IPPROTO_MOBILE;
419 		break;
420 	case SIOCADDMULTI:
421 	case SIOCDELMULTI:
422 		if (ifr == 0) {
423 			error = EAFNOSUPPORT;
424 			break;
425 		}
426 		switch (ifr->ifr_addr.sa_family) {
427 #ifdef INET
428 		case AF_INET:
429 			break;
430 #endif
431 		default:
432 			error = EAFNOSUPPORT;
433 			break;
434 		}
435 		break;
436 	case GRESPROTO:
437 		/* Check for superuser */
438 		if ((error = suser(prc, 0)) != 0)
439 			break;
440 
441 		sc->g_proto = ifr->ifr_flags;
442 		switch (sc->g_proto) {
443 		case IPPROTO_GRE:
444 			ifp->if_flags |= IFF_LINK0;
445 			break;
446 		case IPPROTO_MOBILE:
447 			ifp->if_flags &= ~IFF_LINK0;
448 			break;
449 		default:
450 			error = EPROTONOSUPPORT;
451 			break;
452 		}
453 		break;
454 	case GREGPROTO:
455 		ifr->ifr_flags = sc->g_proto;
456 		break;
457 	case GRESADDRS:
458 	case GRESADDRD:
459 		/* Check for superuser */
460 		if ((error = suser(prc, 0)) != 0)
461 			break;
462 
463 		/*
464 		 * set tunnel endpoints, compute a less specific route
465 		 * to the remote end and mark if as up
466 		 */
467 		sa = &ifr->ifr_addr;
468 		if (cmd == GRESADDRS )
469 			sc->g_src = (satosin(sa))->sin_addr;
470 		if (cmd == GRESADDRD )
471 			sc->g_dst = (satosin(sa))->sin_addr;
472 	recompute:
473 		if ((sc->g_src.s_addr != INADDR_ANY) &&
474 		    (sc->g_dst.s_addr != INADDR_ANY)) {
475 			if (sc->route.ro_rt != 0) {
476 				/* free old route */
477 				RTFREE(sc->route.ro_rt);
478 				sc->route.ro_rt = (struct rtentry *) 0;
479 			}
480 
481 			gre_compute_route(sc);
482 			if (sc->route.ro_rt == 0)
483 			{
484 				sc->g_src.s_addr = INADDR_ANY;
485 				sc->g_dst.s_addr = INADDR_ANY;
486 				splx(s);
487 				return EIO; /* Is this is good ? */
488 			}
489 			ifp->if_flags |= IFF_UP;
490 		}
491 		break;
492 	case GREGADDRS:
493 		bzero(&si, sizeof(si));
494 		si.sin_family = AF_INET;
495 		si.sin_len = sizeof(struct sockaddr_in);
496 		si.sin_addr.s_addr = sc->g_src.s_addr;
497 		sa = sintosa(&si);
498 		ifr->ifr_addr = *sa;
499 		break;
500 	case GREGADDRD:
501 		bzero(&si, sizeof(si));
502 		si.sin_family = AF_INET;
503 		si.sin_len = sizeof(struct sockaddr_in);
504 		si.sin_addr.s_addr = sc->g_dst.s_addr;
505 		sa = sintosa(&si);
506 		ifr->ifr_addr = *sa;
507 		break;
508 	case SIOCSLIFPHYADDR:
509 		if ((error = suser(prc, 0)) != 0)
510 			break;
511 		if (lifr->addr.ss_family != AF_INET ||
512 		    lifr->dstaddr.ss_family != AF_INET) {
513 			error = EAFNOSUPPORT;
514 			break;
515 		}
516 		if (lifr->addr.ss_len != sizeof(si) ||
517 		    lifr->dstaddr.ss_len != sizeof(si)) {
518 			error = EINVAL;
519 			break;
520 		}
521 		sc->g_src = (satosin((struct sockadrr *)&lifr->addr))->sin_addr;
522 		sc->g_dst =
523 		    (satosin((struct sockadrr *)&lifr->dstaddr))->sin_addr;
524 		goto recompute;
525 	case SIOCDIFPHYADDR:
526 		if ((error = suser(prc, 0)) != 0)
527 			break;
528 		sc->g_src.s_addr = INADDR_ANY;
529 		sc->g_dst.s_addr = INADDR_ANY;
530 		break;
531 	case SIOCGLIFPHYADDR:
532 		if (sc->g_src.s_addr == INADDR_ANY ||
533 		    sc->g_dst.s_addr == INADDR_ANY) {
534 			error = EADDRNOTAVAIL;
535 			break;
536 		}
537 		bzero(&si, sizeof(si));
538 		si.sin_family = AF_INET;
539 		si.sin_len = sizeof(struct sockaddr_in);
540 		si.sin_addr.s_addr = sc->g_src.s_addr;
541 		memcpy(&lifr->addr, &si, sizeof(si));
542 		si.sin_addr.s_addr = sc->g_dst.s_addr;
543 		memcpy(&lifr->dstaddr, &si, sizeof(si));
544 		break;
545 	default:
546 		error = EINVAL;
547 	}
548 
549 	splx(s);
550 	return (error);
551 }
552 
553 /*
554  * computes a route to our destination that is not the one
555  * which would be taken by ip_output(), as this one will loop back to
556  * us. If the interface is p2p as  a--->b, then a routing entry exists
557  * If we now send a packet to b (e.g. ping b), this will come down here
558  * gets src=a, dst=b tacked on and would from ip_output() sent back to
559  * if_gre.
560  * Goal here is to compute a route to b that is less specific than
561  * a-->b. We know that this one exists as in normal operation we have
562  * at least a default route which matches.
563  */
564 
565 static void
566 gre_compute_route(struct gre_softc *sc)
567 {
568 	struct route *ro;
569 	u_int32_t a, b, c;
570 
571 	ro = &sc->route;
572 
573 	bzero(ro, sizeof(struct route));
574 	((struct sockaddr_in *) &ro->ro_dst)->sin_addr = sc->g_dst;
575 	ro->ro_dst.sa_family = AF_INET;
576 	ro->ro_dst.sa_len = sizeof(ro->ro_dst);
577 
578 	/*
579 	 * toggle last bit, so our interface is not found, but a less
580 	 * specific route. I'd rather like to specify a shorter mask,
581  	 * but this is not possible. Should work though. XXX
582 	 * there is a simpler way ...
583 	 */
584 	if ((sc->sc_if.if_flags & IFF_LINK1) == 0) {
585 		a = ntohl(sc->g_dst.s_addr);
586 		b = a & 0x01;
587 		c = a & 0xfffffffe;
588 		b = b ^ 0x01;
589 		a = b | c;
590 		((struct sockaddr_in *) &ro->ro_dst)->sin_addr.s_addr = htonl(a);
591 	}
592 
593 	rtalloc(ro);
594 	if (ro->ro_rt == 0)
595 		return;
596 
597 	/*
598 	 * Check whether we just created a loop. An even more paranoid
599 	 * check would be against all GRE interfaces, but that would
600 	 * not allow people to link GRE tunnels.
601 	 */
602 	if (ro->ro_rt->rt_ifp == &sc->sc_if) {
603 		RTFREE(ro->ro_rt);
604 		ro->ro_rt = (struct rtentry *) 0;
605 		return;
606 	}
607 
608 	/*
609 	 * now change it back - else ip_output will just drop
610 	 * the route and search one to this interface ...
611 	 */
612 	if ((sc->sc_if.if_flags & IFF_LINK1) == 0)
613 		((struct sockaddr_in *) &ro->ro_dst)->sin_addr = sc->g_dst;
614 }
615 
616 /*
617  * do a checksum of a buffer - much like in_cksum, which operates on
618  * mbufs.
619  */
620 u_short
621 gre_in_cksum(u_short *p, u_int len)
622 {
623 	u_int sum = 0;
624 	int nwords = len >> 1;
625 
626 	while (nwords-- != 0)
627 		sum += *p++;
628 
629 		if (len & 1) {
630 			union {
631 				u_short w;
632 				u_char c[2];
633 			} u;
634 			u.c[0] = *(u_char *) p;
635 			u.c[1] = 0;
636 			sum += u.w;
637 		}
638 
639 		/* end-around-carry */
640 		sum = (sum >> 16) + (sum & 0xffff);
641 		sum += (sum >> 16);
642 		return (~sum);
643 }
644 #endif
645