]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/ah_output.c
fa6388fbc8dfdb1d3114c9959efffaaa45128081
[apple/xnu.git] / bsd / netinet6 / ah_output.c
1 /*
2 * Copyright (c) 2008-2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /* $FreeBSD: src/sys/netinet6/ah_output.c,v 1.1.2.3 2001/07/03 11:01:49 ume Exp $ */
30 /* $KAME: ah_output.c,v 1.30 2001/02/21 00:50:53 itojun Exp $ */
31
32 /*
33 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the project nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61 /*
62 * RFC1826/2402 authentication header.
63 */
64
65 #define _IP_VHL
66
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/malloc.h>
70 #include <sys/mbuf.h>
71 #include <sys/domain.h>
72 #include <sys/protosw.h>
73 #include <sys/socket.h>
74 #include <sys/socketvar.h>
75 #include <sys/errno.h>
76 #include <sys/time.h>
77 #include <sys/kernel.h>
78 #include <sys/syslog.h>
79
80 #include <net/if.h>
81 #include <net/route.h>
82
83 #include <netinet/in.h>
84
85 #include <netinet/in_systm.h>
86 #include <netinet/ip.h>
87 #include <netinet/in_var.h>
88
89 #include <netinet/ip6.h>
90 #include <netinet6/ip6_var.h>
91 #include <netinet/icmp6.h>
92
93 #include <netinet6/ipsec.h>
94 #include <netinet6/ipsec6.h>
95 #include <netinet6/ah.h>
96 #include <netinet6/ah6.h>
97 #include <netkey/key.h>
98 #include <netkey/keydb.h>
99
100 #include <net/net_osdep.h>
101
102 #if INET
103 static struct in_addr *ah4_finaldst(struct mbuf *);
104 #endif
105
106 extern lck_mtx_t *sadb_mutex;
107
108 /*
109 * compute AH header size.
110 * transport mode only. for tunnel mode, we should implement
111 * virtual interface, and control MTU/MSS by the interface MTU.
112 */
113 size_t
114 ah_hdrsiz(struct ipsecrequest *isr)
115 {
116 /* sanity check */
117 if (isr == NULL) {
118 panic("ah_hdrsiz: NULL was passed.\n");
119 }
120
121 if (isr->saidx.proto != IPPROTO_AH) {
122 panic("unsupported mode passed to ah_hdrsiz");
123 }
124
125 #if 0
126 {
127 lck_mtx_lock(sadb_mutex);
128 const struct ah_algorithm *algo;
129 size_t hdrsiz;
130
131 /*%%%%% this needs to change - no sav in ipsecrequest any more */
132 if (isr->sav == NULL) {
133 goto estimate;
134 }
135 if (isr->sav->state != SADB_SASTATE_MATURE
136 && isr->sav->state != SADB_SASTATE_DYING) {
137 goto estimate;
138 }
139
140 /* we need transport mode AH. */
141 algo = ah_algorithm_lookup(isr->sav->alg_auth);
142 if (!algo) {
143 goto estimate;
144 }
145
146 /*
147 * XXX
148 * right now we don't calcurate the padding size. simply
149 * treat the padding size as constant, for simplicity.
150 *
151 * XXX variable size padding support
152 */
153 hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1));
154 if (isr->sav->flags & SADB_X_EXT_OLD) {
155 hdrsiz += sizeof(struct ah);
156 } else {
157 hdrsiz += sizeof(struct newah);
158 }
159
160 lck_mtx_unlock(sadb_mutex);
161 return hdrsiz;
162 }
163
164 estimate:
165 #endif
166
167 //lck_mtx_unlock(sadb_mutex);
168 /* ASSUMING:
169 * sizeof(struct newah) > sizeof(struct ah).
170 * 16 = (16 + 3) & ~(4 - 1).
171 */
172 return sizeof(struct newah) + 16;
173 }
174
175 #if INET
176 /*
177 * Modify the packet so that it includes the authentication data.
178 * The mbuf passed must start with IPv4 header.
179 *
180 * assumes that the first mbuf contains IPv4 header + option only.
181 * the function does not modify m.
182 */
183 int
184 ah4_output(struct mbuf *m, struct secasvar *sav)
185 {
186 const struct ah_algorithm *algo;
187 u_int32_t spi;
188 u_char *ahdrpos;
189 u_char *ahsumpos = NULL;
190 size_t hlen = 0; /*IP header+option in bytes*/
191 size_t plen = 0; /*AH payload size in bytes*/
192 size_t ahlen = 0; /*plen + sizeof(ah)*/
193 struct ip *ip;
194 struct in_addr dst = { .s_addr = 0 };
195 struct in_addr *finaldst;
196 int error;
197
198 /* sanity checks */
199 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] == NULL) {
200 ip = mtod(m, struct ip *);
201 ipseclog((LOG_DEBUG, "ah4_output: internal error: "
202 "sav->replay is null: %x->%x, SPI=%u\n",
203 (u_int32_t)ntohl(ip->ip_src.s_addr),
204 (u_int32_t)ntohl(ip->ip_dst.s_addr),
205 (u_int32_t)ntohl(sav->spi)));
206 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
207 m_freem(m);
208 return EINVAL;
209 }
210
211 algo = ah_algorithm_lookup(sav->alg_auth);
212 if (!algo) {
213 ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: "
214 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
215 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
216 m_freem(m);
217 return EINVAL;
218 }
219 spi = sav->spi;
220
221 /*
222 * determine the size to grow.
223 */
224 if (sav->flags & SADB_X_EXT_OLD) {
225 /* RFC 1826 */
226 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
227 ahlen = plen + sizeof(struct ah);
228 } else {
229 /* RFC 2402 */
230 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
231 ahlen = plen + sizeof(struct newah);
232 }
233
234 VERIFY(ahlen <= UINT16_MAX);
235
236 /*
237 * grow the mbuf to accomodate AH.
238 */
239 ip = mtod(m, struct ip *);
240 #ifdef _IP_VHL
241 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
242 #else
243 hlen = ip->ip_hl << 2;
244 #endif
245
246 if (m->m_len != hlen) {
247 panic("ah4_output: assumption failed (first mbuf length)");
248 }
249 if (M_LEADINGSPACE(m->m_next) < ahlen) {
250 struct mbuf *n;
251 MGET(n, M_DONTWAIT, MT_DATA);
252 if (!n) {
253 ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n",
254 __LINE__));
255 m_freem(m);
256 return ENOBUFS;
257 }
258 n->m_len = (int32_t)ahlen;
259 n->m_next = m->m_next;
260 m->m_next = n;
261 m->m_pkthdr.len += ahlen;
262 ahdrpos = mtod(n, u_char *);
263 } else {
264 m->m_next->m_len += ahlen;
265 m->m_next->m_data -= ahlen;
266 m->m_pkthdr.len += ahlen;
267 ahdrpos = mtod(m->m_next, u_char *);
268 }
269
270 ip = mtod(m, struct ip *); /*just to be sure*/
271
272 /*
273 * initialize AH.
274 */
275 if (sav->flags & SADB_X_EXT_OLD) {
276 struct ah *ahdr;
277
278 VERIFY((plen >> 2) <= UINT8_MAX);
279 ahdr = (struct ah *)(void *)ahdrpos;
280 ahsumpos = (u_char *)(ahdr + 1);
281 ahdr->ah_len = (u_int8_t)(plen >> 2);
282 ahdr->ah_nxt = ip->ip_p;
283 ahdr->ah_reserve = htons(0);
284 ahdr->ah_spi = spi;
285 bzero(ahdr + 1, plen);
286 } else {
287 struct newah *ahdr;
288
289 VERIFY(((plen >> 2) + 1) <= UINT8_MAX);
290 ahdr = (struct newah *)(void *)ahdrpos;
291 ahsumpos = (u_char *)(ahdr + 1);
292 ahdr->ah_len = (u_int8_t)((plen >> 2) + 1); /* plus one for seq# */
293 ahdr->ah_nxt = ip->ip_p;
294 ahdr->ah_reserve = htons(0);
295 ahdr->ah_spi = spi;
296 if (sav->replay[0]->count == ~0) {
297 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
298 /* XXX Is it noisy ? */
299 ipseclog((LOG_WARNING,
300 "replay counter overflowed. %s\n",
301 ipsec_logsastr(sav)));
302 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
303 m_freem(m);
304 return EINVAL;
305 }
306 }
307 lck_mtx_lock(sadb_mutex);
308 sav->replay[0]->count++;
309 lck_mtx_unlock(sadb_mutex);
310 /*
311 * XXX sequence number must not be cycled, if the SA is
312 * installed by IKE daemon.
313 */
314 ahdr->ah_seq = htonl(sav->replay[0]->count);
315 bzero(ahdr + 1, plen);
316 }
317
318 /*
319 * modify IPv4 header.
320 */
321 ip->ip_p = IPPROTO_AH;
322 if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len))) {
323 ip->ip_len = htons(ntohs(ip->ip_len) + (u_int16_t)ahlen);
324 } else {
325 ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n"));
326 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
327 m_freem(m);
328 return EMSGSIZE;
329 }
330
331 /*
332 * If there is source routing option, update destination field in
333 * the IPv4 header to the final destination.
334 * Note that we do not need to update source routing option itself
335 * (as done in IPv4 AH processing -- see ip6_output()), since
336 * source routing option is not part of the ICV computation.
337 */
338 finaldst = ah4_finaldst(m);
339 if (finaldst) {
340 dst.s_addr = ip->ip_dst.s_addr;
341 ip->ip_dst.s_addr = finaldst->s_addr;
342 }
343
344 /*
345 * calcurate the checksum, based on security association
346 * and the algorithm specified.
347 */
348 error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
349 if (error) {
350 ipseclog((LOG_ERR,
351 "error after ah4_calccksum, called from ah4_output"));
352 m_freem(m);
353 m = NULL;
354 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
355 return error;
356 }
357
358 if (finaldst) {
359 ip = mtod(m, struct ip *); /*just to make sure*/
360 ip->ip_dst.s_addr = dst.s_addr;
361 }
362 lck_mtx_lock(sadb_stat_mutex);
363 ipsecstat.out_success++;
364 ipsecstat.out_ahhist[sav->alg_auth]++;
365 lck_mtx_unlock(sadb_stat_mutex);
366 key_sa_recordxfer(sav, m);
367
368 return 0;
369 }
370 #endif
371
372 /* Calculate AH length */
373 size_t
374 ah_hdrlen(struct secasvar *sav)
375 {
376 const struct ah_algorithm *algo;
377 size_t plen, ahlen;
378
379 algo = ah_algorithm_lookup(sav->alg_auth);
380 if (!algo) {
381 return 0;
382 }
383 if (sav->flags & SADB_X_EXT_OLD) {
384 /* RFC 1826 */
385 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
386 ahlen = plen + sizeof(struct ah);
387 } else {
388 /* RFC 2402 */
389 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
390 ahlen = plen + sizeof(struct newah);
391 }
392
393 return ahlen;
394 }
395
396 /*
397 * Fill in the Authentication Header and calculate checksum.
398 */
399 int
400 ah6_output(struct mbuf *m, u_char *nexthdrp, struct mbuf *md,
401 struct secasvar *sav)
402 {
403 struct mbuf *mprev;
404 struct mbuf *mah;
405 const struct ah_algorithm *algo;
406 u_int32_t spi;
407 u_char *ahsumpos = NULL;
408 size_t plen; /*AH payload size in bytes*/
409 int error = 0;
410 size_t ahlen;
411 struct ip6_hdr *ip6;
412
413 if (m->m_len < sizeof(struct ip6_hdr)) {
414 ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n"));
415 m_freem(m);
416 return EINVAL;
417 }
418
419 ahlen = ah_hdrlen(sav);
420 if (ahlen == 0) {
421 return 0;
422 }
423
424 VERIFY(ahlen <= UINT16_MAX);
425
426 for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) {
427 ;
428 }
429 if (!mprev || mprev->m_next != md) {
430 ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n"));
431 m_freem(m);
432 return EINVAL;
433 }
434
435 MGET(mah, M_DONTWAIT, MT_DATA);
436 if (!mah) {
437 m_freem(m);
438 return ENOBUFS;
439 }
440 if (ahlen > MLEN) {
441 MCLGET(mah, M_DONTWAIT);
442 if ((mah->m_flags & M_EXT) == 0) {
443 m_free(mah);
444 m_freem(m);
445 return ENOBUFS;
446 }
447 }
448 mah->m_len = (int32_t)ahlen;
449 mah->m_next = md;
450 mprev->m_next = mah;
451 m->m_pkthdr.len += ahlen;
452
453 /* fix plen */
454 if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
455 ipseclog((LOG_ERR,
456 "ip6_output: AH with IPv6 jumbogram is not supported\n"));
457 m_freem(m);
458 return EINVAL;
459 }
460
461 ip6 = mtod(m, struct ip6_hdr *);
462 ip6->ip6_plen = htons((u_int16_t)(m->m_pkthdr.len - sizeof(struct ip6_hdr)));
463
464 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] == NULL) {
465 ipseclog((LOG_DEBUG, "ah6_output: internal error: "
466 "sav->replay is null: SPI=%u\n",
467 (u_int32_t)ntohl(sav->spi)));
468 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
469 m_freem(m);
470 return EINVAL;
471 }
472
473 algo = ah_algorithm_lookup(sav->alg_auth);
474 if (!algo) {
475 ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: "
476 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
477 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
478 m_freem(m);
479 return EINVAL;
480 }
481 spi = sav->spi;
482
483 /*
484 * initialize AH.
485 */
486 if (sav->flags & SADB_X_EXT_OLD) {
487 struct ah *ahdr = mtod(mah, struct ah *);
488
489 plen = mah->m_len - sizeof(struct ah);
490 VERIFY((plen >> 2) <= UINT8_MAX);
491 ahsumpos = (u_char *)(ahdr + 1);
492 ahdr->ah_nxt = *nexthdrp;
493 *nexthdrp = IPPROTO_AH;
494 ahdr->ah_len = (u_int8_t)(plen >> 2);
495 ahdr->ah_reserve = htons(0);
496 ahdr->ah_spi = spi;
497 bzero(ahdr + 1, plen);
498 } else {
499 struct newah *ahdr = mtod(mah, struct newah *);
500
501 plen = mah->m_len - sizeof(struct newah);
502 VERIFY(((plen >> 2) + 1) <= UINT8_MAX);
503 ahsumpos = (u_char *)(ahdr + 1);
504 ahdr->ah_nxt = *nexthdrp;
505 *nexthdrp = IPPROTO_AH;
506 ahdr->ah_len = (u_int8_t)((plen >> 2) + 1); /* plus one for seq# */
507 ahdr->ah_reserve = htons(0);
508 ahdr->ah_spi = spi;
509 if (sav->replay[0]->count == ~0) {
510 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
511 /* XXX Is it noisy ? */
512 ipseclog((LOG_WARNING,
513 "replay counter overflowed. %s\n",
514 ipsec_logsastr(sav)));
515 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
516 m_freem(m);
517 return EINVAL;
518 }
519 }
520 lck_mtx_lock(sadb_mutex);
521 sav->replay[0]->count++;
522 lck_mtx_unlock(sadb_mutex);
523 /*
524 * XXX sequence number must not be cycled, if the SA is
525 * installed by IKE daemon.
526 */
527 ahdr->ah_seq = htonl(sav->replay[0]->count);
528 bzero(ahdr + 1, plen);
529 }
530
531 /*
532 * calcurate the checksum, based on security association
533 * and the algorithm specified.
534 */
535 error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
536 if (error) {
537 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
538 m_freem(m);
539 } else {
540 IPSEC_STAT_INCREMENT(ipsec6stat.out_success);
541 key_sa_recordxfer(sav, m);
542 }
543 IPSEC_STAT_INCREMENT(ipsec6stat.out_ahhist[sav->alg_auth]);
544
545 return error;
546 }
547
548 /*
549 * Find the final destination if there is loose/strict source routing option.
550 * Returns NULL if there's no source routing options.
551 * Returns NULL on errors too.
552 * Note that this function will return a pointer INTO the given parameter,
553 * struct mbuf *m.
554 * The mbuf must be pulled up toward, at least, ip option part.
555 */
556 static struct in_addr *
557 ah4_finaldst(struct mbuf *m)
558 {
559 struct ip *ip;
560 int optlen;
561 u_char *q;
562 int i;
563 int hlen;
564
565 if (!m) {
566 panic("ah4_finaldst: m == NULL");
567 }
568 ip = mtod(m, struct ip *);
569 #ifdef _IP_VHL
570 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
571 #else
572 hlen = ip->ip_hl << 2;
573 #endif
574
575 if (m->m_len < hlen) {
576 ipseclog((LOG_DEBUG,
577 "ah4_finaldst: parameter mbuf wrong (not pulled up)\n"));
578 return NULL;
579 }
580
581 if (hlen == sizeof(struct ip)) {
582 return NULL;
583 }
584
585 optlen = hlen - sizeof(struct ip);
586 if (optlen < 0) {
587 ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n",
588 optlen));
589 return NULL;
590 }
591
592 q = (u_char *)(ip + 1);
593 i = 0;
594 while (i < optlen) {
595 if (i + IPOPT_OPTVAL >= optlen) {
596 return NULL;
597 }
598 if (q[i + IPOPT_OPTVAL] == IPOPT_EOL ||
599 q[i + IPOPT_OPTVAL] == IPOPT_NOP ||
600 i + IPOPT_OLEN < optlen) {
601 ;
602 } else {
603 return NULL;
604 }
605
606 switch (q[i + IPOPT_OPTVAL]) {
607 case IPOPT_EOL:
608 i = optlen; /* bye */
609 break;
610 case IPOPT_NOP:
611 i++;
612 break;
613 case IPOPT_LSRR:
614 case IPOPT_SSRR:
615 if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) ||
616 optlen - i < q[i + IPOPT_OLEN]) {
617 ipseclog((LOG_ERR,
618 "ip_finaldst: invalid IP option "
619 "(code=%02x len=%02x)\n",
620 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
621 return NULL;
622 }
623 i += q[i + IPOPT_OLEN] - sizeof(struct in_addr);
624 return (struct in_addr *)(void *)(q + i);
625 default:
626 if (q[i + IPOPT_OLEN] < 2 ||
627 optlen - i < q[i + IPOPT_OLEN]) {
628 ipseclog((LOG_ERR,
629 "ip_finaldst: invalid IP option "
630 "(code=%02x len=%02x)\n",
631 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
632 return NULL;
633 }
634 i += q[i + IPOPT_OLEN];
635 break;
636 }
637 }
638 return NULL;
639 }