xref: /openbsd/sys/netinet/ipsec_output.c (revision 7c6c9ed7)
1 /*	$OpenBSD: ipsec_output.c,v 1.99 2024/12/27 10:15:09 mvs Exp $ */
2 /*
3  * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
4  *
5  * Copyright (c) 2000-2001 Angelos D. Keromytis.
6  *
7  * Permission to use, copy, and modify this software with or without fee
8  * is hereby granted, provided that this entire notice is included in
9  * all copies of any software which is or includes a copy or
10  * modification of this software.
11  * You may use this code under the GNU public license if you so wish. Please
12  * contribute changes back to the authors under this freer than GPL license
13  * so that we may further the use of strong encryption without limitations to
14  * all.
15  *
16  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
18  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
19  * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
20  * PURPOSE.
21  */
22 
23 #include "pf.h"
24 
25 #include <sys/param.h>
26 #include <sys/systm.h>
27 #include <sys/mbuf.h>
28 #include <sys/socket.h>
29 #include <sys/kernel.h>
30 #include <sys/timeout.h>
31 
32 #include <net/if.h>
33 #include <net/route.h>
34 
35 #include <netinet/in.h>
36 #include <netinet/ip.h>
37 #include <netinet/in_pcb.h>
38 #include <netinet/ip_var.h>
39 #include <netinet6/ip6_var.h>
40 
41 #if NPF > 0
42 #include <net/pfvar.h>
43 #endif
44 
45 #include <netinet/udp.h>
46 #include <netinet/ip_ipip.h>
47 #include <netinet/ip_ah.h>
48 #include <netinet/ip_esp.h>
49 #include <netinet/ip_ipcomp.h>
50 
51 #include <crypto/cryptodev.h>
52 #include <crypto/xform.h>
53 
54 #ifdef ENCDEBUG
55 #define DPRINTF(fmt, args...)						\
56 	do {								\
57 		if (encdebug)						\
58 			printf("%s: " fmt "\n", __func__, ## args);	\
59 	} while (0)
60 #else
61 #define DPRINTF(fmt, args...)						\
62 	do { } while (0)
63 #endif
64 
65 int	udpencap_enable = 1;	/* enabled by default */
66 int	udpencap_port = 4500;	/* triggers decapsulation */
67 
68 /*
69  * Loop over a tdb chain, taking into consideration protocol tunneling. The
70  * fourth argument is set if the first encapsulation header is already in
71  * place.
72  */
73 int
ipsp_process_packet(struct mbuf * m,struct tdb * tdb,int af,int tunalready)74 ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
75 {
76 	int hlen, off, error;
77 #ifdef INET6
78 	struct ip6_ext ip6e;
79 	int nxt;
80 	int dstopt = 0;
81 #endif
82 
83 	int setdf = 0;
84 	struct ip *ip;
85 #ifdef INET6
86 	struct ip6_hdr *ip6;
87 #endif /* INET6 */
88 
89 #ifdef ENCDEBUG
90 	char buf[INET6_ADDRSTRLEN];
91 #endif
92 
93 	/* Check that the transform is allowed by the administrator. */
94 	if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) ||
95 	    (tdb->tdb_sproto == IPPROTO_AH && !atomic_load_int(&ah_enable)) ||
96 	    (tdb->tdb_sproto == IPPROTO_IPCOMP &&
97 	    !atomic_load_int(&ipcomp_enable))) {
98 		DPRINTF("IPsec outbound packet dropped due to policy "
99 		    "(check your sysctls)");
100 		error = EHOSTUNREACH;
101 		goto drop;
102 	}
103 
104 	/* Sanity check. */
105 	if (!tdb->tdb_xform) {
106 		DPRINTF("uninitialized TDB");
107 		error = EHOSTUNREACH;
108 		goto drop;
109 	}
110 
111 	/* Check if the SPI is invalid. */
112 	if (tdb->tdb_flags & TDBF_INVALID) {
113 		DPRINTF("attempt to use invalid SA %s/%08x/%u",
114 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
115 		    ntohl(tdb->tdb_spi), tdb->tdb_sproto);
116 		error = ENXIO;
117 		goto drop;
118 	}
119 
120 	/* Check that the network protocol is supported */
121 	switch (tdb->tdb_dst.sa.sa_family) {
122 	case AF_INET:
123 		break;
124 
125 #ifdef INET6
126 	case AF_INET6:
127 		break;
128 #endif /* INET6 */
129 
130 	default:
131 		DPRINTF("attempt to use SA %s/%08x/%u for protocol family %d",
132 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
133 		    ntohl(tdb->tdb_spi), tdb->tdb_sproto,
134 		    tdb->tdb_dst.sa.sa_family);
135 		error = EPFNOSUPPORT;
136 		goto drop;
137 	}
138 
139 	/*
140 	 * Register first use if applicable, setup relevant expiration timer.
141 	 */
142 	if (tdb->tdb_first_use == 0) {
143 		tdb->tdb_first_use = gettime();
144 		if (tdb->tdb_flags & TDBF_FIRSTUSE) {
145 			if (timeout_add_sec(&tdb->tdb_first_tmo,
146 			    tdb->tdb_exp_first_use))
147 				tdb_ref(tdb);
148 		}
149 		if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) {
150 			if (timeout_add_sec(&tdb->tdb_sfirst_tmo,
151 			    tdb->tdb_soft_first_use))
152 				tdb_ref(tdb);
153 		}
154 	}
155 
156 	/*
157 	 * Check for tunneling if we don't have the first header in place.
158 	 * When doing Ethernet-over-IP, we are handed an already-encapsulated
159 	 * frame, so we don't need to re-encapsulate.
160 	 */
161 	if (tunalready == 0) {
162 		/*
163 		 * If the target protocol family is different, we know we'll be
164 		 * doing tunneling.
165 		 */
166 		if (af == tdb->tdb_dst.sa.sa_family) {
167 			switch (af) {
168 			case AF_INET:
169 				hlen = sizeof(struct ip);
170 				break;
171 #ifdef INET6
172 			case AF_INET6:
173 				hlen = sizeof(struct ip6_hdr);
174 				break;
175 #endif /* INET6 */
176 			}
177 
178 			/* Bring the network header in the first mbuf. */
179 			if (m->m_len < hlen) {
180 				if ((m = m_pullup(m, hlen)) == NULL) {
181 					error = ENOBUFS;
182 					goto drop;
183 				}
184 			}
185 
186 			if (af == AF_INET) {
187 				ip = mtod(m, struct ip *);
188 
189 				/*
190 				 * This is not a bridge packet, remember if we
191 				 * had IP_DF.
192 				 */
193 				setdf = ip->ip_off & htons(IP_DF);
194 			}
195 
196 #ifdef INET6
197 			if (af == AF_INET6)
198 				ip6 = mtod(m, struct ip6_hdr *);
199 #endif /* INET6 */
200 		}
201 
202 		/* Do the appropriate encapsulation, if necessary. */
203 		if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */
204 		    (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling needed */
205 		    (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */
206 		    ((tdb->tdb_dst.sa.sa_family == AF_INET) &&
207 		     (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) &&
208 		     (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) ||
209 #ifdef INET6
210 		    ((tdb->tdb_dst.sa.sa_family == AF_INET6) &&
211 		     (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) &&
212 		     (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr,
213 		      &ip6->ip6_dst))) ||
214 #endif /* INET6 */
215 		    0) {
216 			/* Fix IPv4 header checksum and length. */
217 			if (af == AF_INET) {
218 				if (m->m_len < sizeof(struct ip))
219 					if ((m = m_pullup(m,
220 					    sizeof(struct ip))) == NULL) {
221 						error = ENOBUFS;
222 						goto drop;
223 					}
224 
225 				ip = mtod(m, struct ip *);
226 				ip->ip_len = htons(m->m_pkthdr.len);
227 				ip->ip_sum = 0;
228 				ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
229 			}
230 
231 #ifdef INET6
232 			/* Fix IPv6 header payload length. */
233 			if (af == AF_INET6) {
234 				if (m->m_len < sizeof(struct ip6_hdr))
235 					if ((m = m_pullup(m,
236 					    sizeof(struct ip6_hdr))) == NULL) {
237 						error = ENOBUFS;
238 						goto drop;
239 					}
240 
241 				if (m->m_pkthdr.len - sizeof(*ip6) >
242 				    IPV6_MAXPACKET) {
243 					/* No jumbogram support. */
244 					error = ENXIO;	/*?*/
245 					goto drop;
246 				}
247 				ip6 = mtod(m, struct ip6_hdr *);
248 				ip6->ip6_plen = htons(m->m_pkthdr.len
249 				    - sizeof(*ip6));
250 			}
251 #endif /* INET6 */
252 
253 			/* Encapsulate -- m may be changed or set to NULL. */
254 			error = ipip_output(&m, tdb);
255 			if ((m == NULL) && (!error))
256 				error = EFAULT;
257 			if (error)
258 				goto drop;
259 
260 			if (tdb->tdb_dst.sa.sa_family == AF_INET && setdf) {
261 				if (m->m_len < sizeof(struct ip))
262 					if ((m = m_pullup(m,
263 					    sizeof(struct ip))) == NULL) {
264 						error = ENOBUFS;
265 						goto drop;
266 					}
267 
268 				ip = mtod(m, struct ip *);
269 				ip->ip_off |= htons(IP_DF);
270 			}
271 
272 			/* Remember that we appended a tunnel header. */
273 			mtx_enter(&tdb->tdb_mtx);
274 			tdb->tdb_flags |= TDBF_USEDTUNNEL;
275 			mtx_leave(&tdb->tdb_mtx);
276 		}
277 	}
278 
279 	/*
280 	 * If this is just an IP-IP TDB and we're told there's already an
281 	 * encapsulation header or ipip_output() has encapsulated it, move on.
282 	 */
283 	if (tdb->tdb_xform->xf_type == XF_IP4)
284 		return ipsp_process_done(m, tdb);
285 
286 	/* Extract some information off the headers. */
287 	switch (tdb->tdb_dst.sa.sa_family) {
288 	case AF_INET:
289 		ip = mtod(m, struct ip *);
290 		hlen = ip->ip_hl << 2;
291 		off = offsetof(struct ip, ip_p);
292 		break;
293 
294 #ifdef INET6
295 	case AF_INET6:
296 		ip6 = mtod(m, struct ip6_hdr *);
297 		hlen = sizeof(struct ip6_hdr);
298 		off = offsetof(struct ip6_hdr, ip6_nxt);
299 		nxt = ip6->ip6_nxt;
300 		/*
301 		 * chase mbuf chain to find the appropriate place to
302 		 * put AH/ESP/IPcomp header.
303 		 *	IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
304 		 */
305 		do {
306 			switch (nxt) {
307 			case IPPROTO_AH:
308 			case IPPROTO_ESP:
309 			case IPPROTO_IPCOMP:
310 				/*
311 				 * we should not skip security header added
312 				 * beforehand.
313 				 */
314 				goto exitip6loop;
315 
316 			case IPPROTO_HOPOPTS:
317 			case IPPROTO_DSTOPTS:
318 			case IPPROTO_ROUTING:
319 				/*
320 				 * if we see 2nd destination option header,
321 				 * we should stop there.
322 				 */
323 				if (nxt == IPPROTO_DSTOPTS && dstopt)
324 					goto exitip6loop;
325 
326 				if (nxt == IPPROTO_DSTOPTS) {
327 					/*
328 					 * seen 1st or 2nd destination option.
329 					 * next time we see one, it must be 2nd.
330 					 */
331 					dstopt = 1;
332 				} else if (nxt == IPPROTO_ROUTING) {
333 					/*
334 					 * if we see destination option next
335 					 * time, it must be dest2.
336 					 */
337 					dstopt = 2;
338 				}
339 				if (m->m_pkthdr.len < hlen + sizeof(ip6e)) {
340 					error = EINVAL;
341 					goto drop;
342 				}
343 				/* skip this header */
344 				m_copydata(m, hlen, sizeof(ip6e),
345 				    (caddr_t)&ip6e);
346 				nxt = ip6e.ip6e_nxt;
347 				off = hlen + offsetof(struct ip6_ext, ip6e_nxt);
348 				/*
349 				 * we will never see nxt == IPPROTO_AH
350 				 * so it is safe to omit AH case.
351 				 */
352 				hlen += (ip6e.ip6e_len + 1) << 3;
353 				break;
354 			default:
355 				goto exitip6loop;
356 			}
357 		} while (hlen < m->m_pkthdr.len);
358 	exitip6loop:
359 		break;
360 #endif /* INET6 */
361 	default:
362 		error = EPFNOSUPPORT;
363 		goto drop;
364 	}
365 
366 	if (m->m_pkthdr.len < hlen) {
367 		error = EINVAL;
368 		goto drop;
369 	}
370 
371 	ipsecstat_add(ipsec_ouncompbytes, m->m_pkthdr.len);
372 	tdbstat_add(tdb, tdb_ouncompbytes, m->m_pkthdr.len);
373 
374 	/* Non expansion policy for IPCOMP */
375 	if (tdb->tdb_sproto == IPPROTO_IPCOMP) {
376 		if ((m->m_pkthdr.len - hlen) < tdb->tdb_compalgxform->minlen) {
377 			/* No need to compress, leave the packet untouched */
378 			ipcompstat_inc(ipcomps_minlen);
379 			return ipsp_process_done(m, tdb);
380 		}
381 	}
382 
383 	/* Invoke the IPsec transform. */
384 	return (*(tdb->tdb_xform->xf_output))(m, tdb, hlen, off);
385 
386  drop:
387 	m_freem(m);
388 	return error;
389 }
390 
391 /*
392  * Called by the IPsec output transform callbacks, to transmit the packet
393  * or do further processing, as necessary.
394  */
395 int
ipsp_process_done(struct mbuf * m,struct tdb * tdb)396 ipsp_process_done(struct mbuf *m, struct tdb *tdb)
397 {
398 	struct ip *ip;
399 #ifdef INET6
400 	struct ip6_hdr *ip6;
401 #endif /* INET6 */
402 	struct tdb *tdbo;
403 	struct tdb_ident *tdbi;
404 	struct m_tag *mtag;
405 	int roff, error;
406 
407 	NET_ASSERT_LOCKED();
408 
409 	tdb->tdb_last_used = gettime();
410 
411 	if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) {
412 		struct mbuf *mi;
413 		struct udphdr *uh;
414 		int iphlen;
415 
416 		if (!udpencap_enable || !udpencap_port) {
417 			error = ENXIO;
418 			goto drop;
419 		}
420 
421 		switch (tdb->tdb_dst.sa.sa_family) {
422 		case AF_INET:
423 			iphlen = sizeof(struct ip);
424 			break;
425 #ifdef INET6
426 		case AF_INET6:
427 			iphlen = sizeof(struct ip6_hdr);
428 			break;
429 #endif /* INET6 */
430 		default:
431 			DPRINTF("unknown protocol family (%d)",
432 			    tdb->tdb_dst.sa.sa_family);
433 			error = EPFNOSUPPORT;
434 			goto drop;
435 		}
436 
437 		mi = m_makespace(m, iphlen, sizeof(struct udphdr), &roff);
438 		if (mi == NULL) {
439 			error = ENOMEM;
440 			goto drop;
441 		}
442 		uh = (struct udphdr *)(mtod(mi, caddr_t) + roff);
443 		uh->uh_sport = uh->uh_dport = htons(udpencap_port);
444 		if (tdb->tdb_udpencap_port)
445 			uh->uh_dport = tdb->tdb_udpencap_port;
446 
447 		uh->uh_ulen = htons(m->m_pkthdr.len - iphlen);
448 		uh->uh_sum = 0;
449 #ifdef INET6
450 		if (tdb->tdb_dst.sa.sa_family == AF_INET6)
451 			m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
452 #endif /* INET6 */
453 		espstat_inc(esps_udpencout);
454 	}
455 
456 	switch (tdb->tdb_dst.sa.sa_family) {
457 	case AF_INET:
458 		/* Fix the header length, for AH processing. */
459 		ip = mtod(m, struct ip *);
460 		ip->ip_len = htons(m->m_pkthdr.len);
461 		if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
462 			ip->ip_p = IPPROTO_UDP;
463 		break;
464 
465 #ifdef INET6
466 	case AF_INET6:
467 		/* Fix the header length, for AH processing. */
468 		if (m->m_pkthdr.len < sizeof(*ip6)) {
469 			error = ENXIO;
470 			goto drop;
471 		}
472 		if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
473 			/* No jumbogram support. */
474 			error = ENXIO;
475 			goto drop;
476 		}
477 		ip6 = mtod(m, struct ip6_hdr *);
478 		ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
479 		if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
480 			ip6->ip6_nxt = IPPROTO_UDP;
481 		break;
482 #endif /* INET6 */
483 
484 	default:
485 		DPRINTF("unknown protocol family (%d)",
486 		    tdb->tdb_dst.sa.sa_family);
487 		error = EPFNOSUPPORT;
488 		goto drop;
489 	}
490 
491 	/*
492 	 * Add a record of what we've done or what needs to be done to the
493 	 * packet.
494 	 */
495 	mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(struct tdb_ident),
496 	    M_NOWAIT);
497 	if (mtag == NULL) {
498 		DPRINTF("could not allocate packet tag");
499 		error = ENOMEM;
500 		goto drop;
501 	}
502 
503 	tdbi = (struct tdb_ident *)(mtag + 1);
504 	tdbi->dst = tdb->tdb_dst;
505 	tdbi->proto = tdb->tdb_sproto;
506 	tdbi->spi = tdb->tdb_spi;
507 	tdbi->rdomain = tdb->tdb_rdomain;
508 
509 	m_tag_prepend(m, mtag);
510 
511 	ipsecstat_pkt(ipsec_opackets, ipsec_obytes, m->m_pkthdr.len);
512 	tdbstat_pkt(tdb, tdb_opackets, tdb_obytes, m->m_pkthdr.len);
513 
514 	/* If there's another (bundled) TDB to apply, do so. */
515 	tdbo = tdb_ref(tdb->tdb_onext);
516 	if (tdbo != NULL) {
517 		KERNEL_ASSERT_LOCKED();
518 		error = ipsp_process_packet(m, tdbo,
519 		    tdb->tdb_dst.sa.sa_family, 0);
520 		tdb_unref(tdbo);
521 		return error;
522 	}
523 
524 #if NPF > 0
525 	/* Add pf tag if requested. */
526 	pf_tag_packet(m, tdb->tdb_tag, -1);
527 	pf_pkt_addr_changed(m);
528 #endif
529 	if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
530 		m->m_pkthdr.ph_rtableid = tdb->tdb_rdomain_post;
531 
532 	/*
533 	 * We're done with IPsec processing, transmit the packet using the
534 	 * appropriate network protocol (IP or IPv6). SPD lookup will be
535 	 * performed again there.
536 	 */
537 	switch (tdb->tdb_dst.sa.sa_family) {
538 	case AF_INET:
539 		error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0);
540 		break;
541 #ifdef INET6
542 	case AF_INET6:
543 		/*
544 		 * We don't need massage, IPv6 header fields are always in
545 		 * net endian.
546 		 */
547 		error = ip6_output(m, NULL, NULL, 0, NULL, NULL);
548 		break;
549 #endif /* INET6 */
550 	default:
551 		error = EPFNOSUPPORT;
552 		break;
553 	}
554 	return error;
555 
556  drop:
557 	m_freem(m);
558 	return error;
559 }
560 
561 ssize_t
ipsec_hdrsz(struct tdb * tdbp)562 ipsec_hdrsz(struct tdb *tdbp)
563 {
564 	ssize_t adjust;
565 
566 	switch (tdbp->tdb_sproto) {
567 	case IPPROTO_IPIP:
568 		adjust = 0;
569 		break;
570 
571 	case IPPROTO_ESP:
572 		if (tdbp->tdb_encalgxform == NULL)
573 			return (-1);
574 
575 		/* Header length */
576 		adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen;
577 		if (tdbp->tdb_flags & TDBF_UDPENCAP)
578 			adjust += sizeof(struct udphdr);
579 		/* Authenticator */
580 		if (tdbp->tdb_authalgxform != NULL)
581 			adjust += tdbp->tdb_authalgxform->authsize;
582 		/* Padding */
583 		adjust += MAX(4, tdbp->tdb_encalgxform->blocksize);
584 		break;
585 
586 	case IPPROTO_AH:
587 		if (tdbp->tdb_authalgxform == NULL)
588 			return (-1);
589 
590 		adjust = AH_FLENGTH + sizeof(u_int32_t);
591 		adjust += tdbp->tdb_authalgxform->authsize;
592 		break;
593 
594 	default:
595 		return (-1);
596 	}
597 
598 	if (!(tdbp->tdb_flags & TDBF_TUNNELING) &&
599 	    !(tdbp->tdb_flags & TDBF_USEDTUNNEL))
600 		return (adjust);
601 
602 	switch (tdbp->tdb_dst.sa.sa_family) {
603 	case AF_INET:
604 		adjust += sizeof(struct ip);
605 		break;
606 #ifdef INET6
607 	case AF_INET6:
608 		adjust += sizeof(struct ip6_hdr);
609 		break;
610 #endif /* INET6 */
611 	}
612 
613 	return (adjust);
614 }
615 
616 void
ipsec_adjust_mtu(struct mbuf * m,u_int32_t mtu)617 ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu)
618 {
619 	struct tdb_ident *tdbi;
620 	struct tdb *tdbp;
621 	struct m_tag *mtag;
622 	ssize_t adjust;
623 
624 	NET_ASSERT_LOCKED();
625 
626 	for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL); mtag;
627 	     mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, mtag)) {
628 		tdbi = (struct tdb_ident *)(mtag + 1);
629 		tdbp = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst,
630 		    tdbi->proto);
631 		if (tdbp == NULL)
632 			break;
633 
634 		if ((adjust = ipsec_hdrsz(tdbp)) == -1) {
635 			tdb_unref(tdbp);
636 			break;
637 		}
638 
639 		mtu -= adjust;
640 		tdbp->tdb_mtu = mtu;
641 		tdbp->tdb_mtutimeout = gettime() + ip_mtudisc_timeout;
642 		DPRINTF("spi %08x mtu %d adjust %ld mbuf %p",
643 		    ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust, m);
644 		tdb_unref(tdbp);
645 	}
646 }
647