]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/mip6_io.c
739a1177f35cec10f48833b48e2819d9515bd6dc
[apple/xnu.git] / bsd / netinet6 / mip6_io.c
1 /* $KAME: mip6_io.c,v 1.7 2000/03/25 07:23:53 sumikawa Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996, 1997, 1998, 1999 and 2000 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Copyright (c) 1999 and 2000 Ericsson Radio Systems AB
34 * All rights reserved.
35 *
36 * Author: Conny Larsson <conny.larsson@era.ericsson.se>
37 *
38 */
39
40 #if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
41 #include "opt_inet.h"
42 #endif
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/malloc.h>
47 #include <sys/mbuf.h>
48 #include <sys/domain.h>
49 #include <sys/protosw.h>
50 #include <sys/socket.h>
51 #include <sys/errno.h>
52 #include <sys/time.h>
53 #include <sys/kernel.h>
54 #include <net/if.h>
55 #include <net/if_types.h>
56 #include <net/route.h>
57 #include <netinet/in.h>
58 #include <netinet/in_var.h>
59 #include <netinet/ip6.h>
60 #include <netinet6/ip6_var.h>
61 #include <netinet/icmp6.h>
62 #include <netinet6/mip6.h>
63 #include <netinet6/mip6_common.h>
64
65
66 void (*mip6_icmp6_output_hook)(struct mbuf *) = 0;
67 struct mip6_esm * (*mip6_esm_find_hook)(struct in6_addr *) = 0;
68
69
70 /* Declaration of Global variables. */
71 struct mip6_indata *mip6_inp = NULL;
72 struct mip6_output *mip6_outq = NULL;
73
74
75
76 /*
77 ##############################################################################
78 #
79 # RECEIVING FUNCTIONS
80 # These functions receives the incoming IPv6 packet and further processing of
81 # the packet depends on the content in the packet.
82 #
83 ##############################################################################
84 */
85
86 /*
87 ******************************************************************************
88 * Function: mip6_new_packet
89 * Description: Called once when a new IPv6 packet is received. Resets the
90 * mip6_inp variable needed later when options in the dest-
91 * ination header are validated.
92 * Ret value: 0 if OK. Otherwise IPPROTO_DONE.
93 * Note: A prerequisite for this function is that the AH or ESP header
94 * is included in the same IPv6 packet as the destination header,
95 * i.e we are using transport mode and not tunneling mode.
96 ******************************************************************************
97 */
98 int
99 mip6_new_packet(m)
100 struct mbuf *m; /* Mbuf containing IPv6 header */
101 {
102 /* If memory for global variable mip6_indata already allocated,
103 discard it. */
104 if (mip6_inp != NULL) {
105 if (mip6_inp->bu_opt != NULL)
106 FREE(mip6_inp->bu_opt, M_TEMP);
107 if (mip6_inp->ba_opt != NULL)
108 FREE(mip6_inp->ba_opt, M_TEMP);
109 if (mip6_inp->br_opt != NULL)
110 FREE(mip6_inp->br_opt, M_TEMP);
111 if (mip6_inp->ha_opt != NULL)
112 FREE(mip6_inp->ha_opt, M_TEMP);
113 if (mip6_inp->uid != NULL)
114 FREE(mip6_inp->uid, M_TEMP);
115 if (mip6_inp->coa != NULL)
116 FREE(mip6_inp->coa, M_TEMP);
117 if (mip6_inp->hal != NULL)
118 FREE(mip6_inp->hal, M_TEMP);
119 FREE(mip6_inp, M_TEMP);
120 mip6_inp = NULL;
121 }
122
123 /* Allocate memory for global variable mip6_inp */
124 mip6_inp = (struct mip6_indata *)
125 MALLOC(sizeof(struct mip6_indata), M_TEMP, M_WAITOK);
126 if (mip6_inp == NULL)
127 panic("%s: We should not come here !!!!", __FUNCTION__);
128 bzero(mip6_inp, sizeof(struct mip6_indata));
129
130 return 0;
131 }
132
133
134
135 /*
136 ******************************************************************************
137 * Function: mip6_store_dstopt_pre
138 * Description: Pre-processing used by the hook function.
139 * Ret value: 0 if OK. Otherwise IPPROTO_DONE
140 ******************************************************************************
141 */
142 int
143 mip6_store_dstopt_pre(m, opt, off, dstlen)
144 struct mbuf *m; /* Pointer to the beginning of mbuf */
145 u_int8_t *opt; /* Pointer to the beginning of current option in mbuf */
146 u_int8_t off; /* Offset from beginning of mbuf to end of dest header */
147 u_int8_t dstlen; /* Remaining length of Destination header */
148 {
149 u_int8_t type; /* Destination option type */
150
151 type = *opt;
152 if (type == IP6OPT_BINDING_UPDATE) {
153 if (dstlen < IP6OPT_BUMINLEN) {
154 ip6stat.ip6s_toosmall++;
155 return IPPROTO_DONE;
156 }
157
158 if (mip6_store_dstopt(m, opt, off-dstlen) != 0)
159 return IPPROTO_DONE;
160 } else if (type == IP6OPT_BINDING_ACK) {
161 if (dstlen < IP6OPT_BAMINLEN) {
162 ip6stat.ip6s_toosmall++;
163 return IPPROTO_DONE;
164 }
165
166 if (mip6_store_dstopt(m, opt, off-dstlen) != 0)
167 return IPPROTO_DONE;
168 } else if (type == IP6OPT_BINDING_REQ) {
169 if (dstlen < IP6OPT_BRMINLEN) {
170 ip6stat.ip6s_toosmall++;
171 return IPPROTO_DONE;
172 }
173
174 if (mip6_store_dstopt(m, opt, off-dstlen) != 0)
175 return IPPROTO_DONE;
176 } else if (type == IP6OPT_HOME_ADDRESS) {
177 if (dstlen < IP6OPT_HAMINLEN) {
178 ip6stat.ip6s_toosmall++;
179 return IPPROTO_DONE;
180 }
181
182 if (mip6_store_dstopt(m, opt, off-dstlen) != 0)
183 return IPPROTO_DONE;
184 }
185
186 return 0;
187 }
188
189
190
191 /*
192 ******************************************************************************
193 * Function: mip6_store_dstopt
194 * Description: Save each MIPv6 option from the Destination header continously.
195 * They will be evaluated when the entire destination header has
196 * been read.
197 * Ret value: 0 if OK
198 * Otherwise protocol error code from netinet/in.h
199 ******************************************************************************
200 */
201 int
202 mip6_store_dstopt(mp, opt, optoff)
203 struct mbuf *mp; /* Pointer to the beginning of mbuf */
204 u_int8_t *opt; /* Pointer to the beginning of current option in mbuf */
205 u_int8_t optoff; /* Offset from beginning of mbuf to start of current
206 option */
207 {
208 struct mip6_opt_bu *bu_opt; /* Ptr to BU option data */
209 struct mip6_opt_ba *ba_opt; /* Ptr to BA option data */
210 struct mip6_opt_br *br_opt; /* Ptr to BR option data */
211 struct mip6_opt_ha *ha_opt; /* Ptr to HA option data */
212 int tmplen; /* Tmp length for positioning in option */
213 int totlen; /* Total length of option + sub-option */
214 int error;
215
216 /* Find out what kind of buffer we are dealing with */
217 switch (*opt) {
218 case IP6OPT_BINDING_UPDATE:
219 /* Allocate and store Binding Update option data */
220 mip6_inp->bu_opt = (struct mip6_opt_bu *)
221 MALLOC(sizeof(struct mip6_opt_bu), M_TEMP, M_WAITOK);
222 if (mip6_inp->bu_opt == NULL)
223 return ENOBUFS;
224 bzero(mip6_inp->bu_opt, sizeof(struct mip6_opt_bu));
225
226 bu_opt = mip6_inp->bu_opt;
227 m_copydata(mp, optoff, sizeof(bu_opt->type),
228 (caddr_t)&bu_opt->type);
229 tmplen = sizeof(bu_opt->type);
230 m_copydata(mp, optoff + tmplen, sizeof(bu_opt->len),
231 (caddr_t)&bu_opt->len);
232 tmplen += sizeof(bu_opt->len);
233 m_copydata(mp, optoff + tmplen, sizeof(bu_opt->flags),
234 (caddr_t)&bu_opt->flags);
235 tmplen += sizeof(bu_opt->flags);
236 m_copydata(mp, optoff + tmplen, sizeof(bu_opt->prefix_len),
237 (caddr_t)&bu_opt->prefix_len);
238 tmplen += sizeof(bu_opt->prefix_len);
239 m_copydata(mp, optoff + tmplen, sizeof(bu_opt->seqno),
240 (caddr_t)&bu_opt->seqno);
241 tmplen += sizeof(bu_opt->seqno);
242 m_copydata(mp, optoff + tmplen, sizeof(bu_opt->lifetime),
243 (caddr_t)&bu_opt->lifetime);
244 tmplen += sizeof(bu_opt->lifetime);
245
246 bu_opt->seqno = ntohs(bu_opt->seqno);
247 bu_opt->lifetime = ntohl(bu_opt->lifetime);
248
249 /* Set the BU option present flag */
250 mip6_inp->optflag |= MIP6_DSTOPT_BU;
251
252 /* If sub-options are present, store them as well. */
253 if (bu_opt->len > IP6OPT_BULEN) {
254 totlen = bu_opt->len + 2;
255 error = mip6_store_dstsubopt(mp, opt, optoff, totlen, tmplen);
256 if (error)
257 return error;
258 }
259 break;
260 case IP6OPT_BINDING_ACK:
261 /* Allocate and store all Binding Acknowledgement option data */
262 mip6_inp->ba_opt = (struct mip6_opt_ba *)
263 MALLOC(sizeof(struct mip6_opt_ba), M_TEMP, M_WAITOK);
264 if (mip6_inp->ba_opt == NULL)
265 return ENOBUFS;
266 bzero(mip6_inp->ba_opt, sizeof(struct mip6_opt_ba));
267
268 ba_opt = mip6_inp->ba_opt;
269 m_copydata(mp, optoff, sizeof(ba_opt->type),
270 (caddr_t)&ba_opt->type);
271 tmplen = sizeof(ba_opt->type);
272 m_copydata(mp, optoff + tmplen, sizeof(ba_opt->len),
273 (caddr_t)&ba_opt->len);
274 tmplen += sizeof(ba_opt->len);
275 m_copydata(mp, optoff + tmplen, sizeof(ba_opt->status),
276 (caddr_t)&ba_opt->status);
277 tmplen += sizeof(ba_opt->status);
278 m_copydata(mp, optoff + tmplen, sizeof(ba_opt->seqno),
279 (caddr_t)&ba_opt->seqno);
280 tmplen += sizeof(ba_opt->seqno);
281 m_copydata(mp, optoff + tmplen, sizeof(ba_opt->lifetime),
282 (caddr_t)&ba_opt->lifetime);
283 tmplen += sizeof(ba_opt->lifetime);
284 m_copydata(mp, optoff + tmplen, sizeof(ba_opt->refresh),
285 (caddr_t)&ba_opt->refresh);
286 tmplen += sizeof(ba_opt->refresh);
287
288 ba_opt->seqno = ntohs(ba_opt->seqno);
289 ba_opt->lifetime = ntohl(ba_opt->lifetime);
290 ba_opt->refresh = ntohl(ba_opt->refresh);
291
292 /* Set the BA option present flag */
293 mip6_inp->optflag |= MIP6_DSTOPT_BA;
294
295 /* If sub-options are present, store them as well */
296 if (ba_opt->len > IP6OPT_BALEN) {
297 totlen = ba_opt->len + 2;
298 error = mip6_store_dstsubopt(mp, opt, optoff, totlen, tmplen);
299 if (error)
300 return error;
301 }
302 break;
303 case IP6OPT_BINDING_REQ:
304 /* Allocate and store Binding Update option data */
305 mip6_inp->br_opt = (struct mip6_opt_br *)
306 MALLOC(sizeof(struct mip6_opt_br), M_TEMP, M_WAITOK);
307 if (mip6_inp->br_opt == NULL)
308 return ENOBUFS;
309 bzero(mip6_inp->br_opt, sizeof(struct mip6_opt_br));
310
311 br_opt = mip6_inp->br_opt;
312 m_copydata(mp, optoff, sizeof(br_opt->type),
313 (caddr_t)&br_opt->type);
314 tmplen = sizeof(br_opt->type);
315 m_copydata(mp, optoff + tmplen, sizeof(br_opt->len),
316 (caddr_t)&br_opt->len);
317 tmplen += sizeof(br_opt->len);
318
319 /* Set the BR option present flag */
320 mip6_inp->optflag |= MIP6_DSTOPT_BR;
321
322 /* If sub-options are present, store them as well. */
323 if (br_opt->len > IP6OPT_BRLEN) {
324 totlen = br_opt->len + 2;
325 error = mip6_store_dstsubopt(mp, opt, optoff, totlen, tmplen);
326 if (error)
327 return error;
328 }
329 break;
330 case IP6OPT_HOME_ADDRESS:
331 /* Allocate and store Home Address option data */
332 mip6_inp->ha_opt = (struct mip6_opt_ha *)
333 MALLOC(sizeof(struct mip6_opt_ha), M_TEMP, M_WAITOK);
334 if (mip6_inp->ha_opt == NULL)
335 return ENOBUFS;
336 bzero(mip6_inp->ha_opt, sizeof(struct mip6_opt_ha));
337
338 /* Store Home Address option data */
339 ha_opt = mip6_inp->ha_opt;
340 m_copydata(mp, optoff, sizeof(ha_opt->type),
341 (caddr_t)&ha_opt->type);
342 tmplen = sizeof(ha_opt->type);
343 m_copydata(mp, optoff + tmplen, sizeof(ha_opt->len),
344 (caddr_t)&ha_opt->len);
345 tmplen += sizeof(ha_opt->len);
346 m_copydata(mp, optoff + tmplen, sizeof(ha_opt->home_addr),
347 (caddr_t)&ha_opt->home_addr);
348 tmplen += sizeof(ha_opt->home_addr);
349
350 /* Set the HA option present flag */
351 mip6_inp->optflag |= MIP6_DSTOPT_HA;
352 break;
353 default:
354 /* We will not come here since the calling function knows
355 which options to call this function for. */
356 }
357 return 0;
358 }
359
360
361
362 /*
363 ******************************************************************************
364 * Function: mip6_store_dstsubopt
365 * Description: Save each MIPv6 suboption from the Destination header.
366 * They will be evaluated when the entire destination header has
367 * been read.
368 * Ret value: 0 if OK
369 * Otherwise protocol error code from netinet/in.h
370 ******************************************************************************
371 */
372 int
373 mip6_store_dstsubopt(mp, opt, optoff, totlen, tmplen)
374 struct mbuf *mp; /* Pointer to start of mbuf */
375 u_int8_t *opt; /* Pointer to start of current option in mbuf */
376 u_int8_t optoff; /* Offset from start of mbuf to current option */
377 int totlen; /* Total length for option + sub-options */
378 int tmplen; /* Tmp length for positioning in option */
379 {
380 struct mip6_subopt_hal *hal;
381 struct mip6_subopt_coa *coa;
382 int ii, len;
383
384 /* Loop over the sub-options. */
385 while (tmplen < totlen) {
386 switch (*(opt + tmplen)) {
387 case IP6OPT_PAD1:
388 tmplen += 1;
389 break;
390 case IP6OPT_PADN:
391 tmplen += *(opt + tmplen + 1) + 2;
392 break;
393 case IP6SUBOPT_UNIQUEID:
394 /* Make sure that the length is OK */
395 if (*(opt + tmplen + 1) != IP6OPT_UIDLEN) {
396 MIP6_FREEINDATA;
397 return EIO;
398 }
399
400 /* Allocate and store additional sub-option data */
401 mip6_inp->uid = (struct mip6_subopt_id *)
402 MALLOC(sizeof(struct mip6_subopt_id), M_TEMP, M_WAITOK);
403 if (mip6_inp->uid == NULL)
404 return ENOBUFS;
405 bzero(mip6_inp->uid, sizeof(struct mip6_subopt_id));
406
407 m_copydata(mp, optoff + tmplen, sizeof(struct mip6_subopt_id),
408 (caddr_t)mip6_inp->uid);
409 tmplen += sizeof(struct mip6_subopt_id);
410 mip6_inp->uid->id = ntohs(mip6_inp->uid->id);
411
412 /* Set the Unique Id sub-option present flag */
413 mip6_inp->optflag |= MIP6_DSTOPT_UID;
414 break;
415 case IP6SUBOPT_HALIST:
416 /* Make sure that the length is OK */
417 if (*(opt + tmplen + 1) % IP6OPT_HALISTLEN) {
418 MIP6_FREEINDATA;
419 return EIO;
420 }
421
422 /* Allocate and store additional sub-option data */
423 len = *(opt + tmplen +1) / IP6OPT_HALISTLEN;
424 mip6_inp->hal = (struct mip6_subopt_hal *)
425 MALLOC(sizeof(struct mip6_subopt_hal) +
426 (len - 1) * sizeof(struct in6_addr),
427 M_TEMP, M_WAITOK);
428 if (mip6_inp->hal == NULL) {
429 MIP6_FREEINDATA;
430 return ENOMEM;
431 }
432
433 hal = mip6_inp->hal;
434 m_copydata(mp, optoff + tmplen, sizeof(hal->type),
435 (caddr_t)&hal->type);
436 tmplen += sizeof(hal->type);
437 m_copydata(mp, optoff + tmplen, sizeof(hal->len),
438 (caddr_t)&hal->len);
439 tmplen += sizeof(hal->len);
440
441 /* Loop over the addresses */
442 for (ii = 0; ii < len; ii++) {
443 m_copydata(mp, optoff, tmplen, (caddr_t)&hal->halist[ii]);
444 tmplen += sizeof(struct in6_addr);
445 }
446
447 /* Set the BA HA List sub-option present flag */
448 mip6_inp->optflag |= MIP6_DSTOPT_HAL;
449 break;
450 case IP6SUBOPT_ALTCOA:
451 /* Make sure that the length is OK */
452 if (*(opt + tmplen + 1) != IP6OPT_COALEN) {
453 MIP6_FREEINDATA;
454 return EIO;
455 }
456
457 /* Allocate and store additional sub-option data */
458 mip6_inp->coa = (struct mip6_subopt_coa *)
459 MALLOC(sizeof(struct mip6_subopt_coa), M_TEMP, M_WAITOK);
460 if (mip6_inp->coa == NULL)
461 return ENOBUFS;
462 bzero(mip6_inp->coa, sizeof(struct mip6_subopt_coa));
463
464 coa = mip6_inp->coa;
465 m_copydata(mp, optoff + tmplen, sizeof(coa->type),
466 (caddr_t)&coa->type);
467 tmplen += sizeof(coa->type);
468 m_copydata(mp, optoff + tmplen, sizeof(coa->len),
469 (caddr_t)&coa->len);
470 tmplen += sizeof(coa->len);
471 m_copydata(mp, optoff + tmplen, sizeof(coa->coa),
472 (caddr_t)&coa->coa);
473 tmplen += sizeof(coa->coa);
474
475 /* Set the Alternate COA sub-option present flag */
476 mip6_inp->optflag |= MIP6_DSTOPT_COA;
477 break;
478 default:
479 /* Quietly ignore and skip over the sub-option.
480 No statistics done. */
481 tmplen += *(opt + tmplen + 1) + 2;
482 }
483 }
484 return 0;
485 }
486
487
488
489 /*
490 ##############################################################################
491 #
492 # SENDING FUNCTIONS
493 # Functions used for processing of the outgoing IPv6 packet.
494 #
495 ##############################################################################
496 */
497
498 /*
499 ******************************************************************************
500 * Function: mip6_output
501 * Description: This function is always called by function ip6_output. If there
502 * are any Destination Header options they will be added. A Home
503 * Address option MUST be added if the MN is roaming. Otherwise
504 * nothing is done.
505 * The options are stored in an output queue as a chain of mbufs
506 * associated with a destination address. This approach makes it
507 * possible to send it in any IPv6 packet carrying any payload,
508 * i.e piggy backing.
509 * Ret value: 0 if OK
510 * Otherwise any appropriate error code
511 ******************************************************************************
512 */
513 int
514 mip6_output(m, pktopt)
515 struct mbuf *m; /* Includes IPv6 header */
516 struct ip6_pktopts **pktopt; /* Packet Extension headers, options and data */
517 {
518 struct ip6_pktopts *opt; /* Packet Extension headers (local) */
519 struct mip6_output *outp; /* Ptr to mip6 output element */
520 struct mip6_esm *esp; /* Ptr to entry in event state list */
521 struct ip6_hdr *ip6; /* IPv6 header */
522 struct mip6_bc *bcp; /* Binding Cache list entry */
523 struct mip6_bul *bulp;
524 struct mip6_bul *bulp_hr;
525 struct in6_addr *dst_addr; /* Original dst address for the packet */
526 int error; /* Error code from function call */
527 int off; /* Offset from start of Destination Header in bytes */
528 u_int8_t opttype; /* Option type */
529
530 ip6 = mtod(m, struct ip6_hdr *);
531 opt = *pktopt;
532
533 /* We have to maintain a list of all prefixes announced by the
534 rtadvd deamon (for on-link determination). */
535 if (MIP6_IS_HA_ACTIVE) {
536 if (ip6->ip6_nxt == IPPROTO_ICMPV6)
537 if (mip6_icmp6_output_hook) (*mip6_icmp6_output_hook)(m);
538 }
539
540 /* If a COA for the destination address exist, i.e a BC entry is found,
541 then add a Routing Header and change the destination address to the
542 MN's COA. */
543 dst_addr = &ip6->ip6_dst;
544 bcp = mip6_bc_find(&ip6->ip6_dst);
545 if (bcp != NULL) {
546 dst_addr = &bcp->home_addr;
547 if ((error = mip6_add_rh(&opt, bcp)) != 0)
548 return error;
549 }
550
551 /* If this is a MN and the source address is one of the home addresses
552 for the MN then a Home Address option must be inserted. */
553 esp = NULL;
554 if (MIP6_IS_MN_ACTIVE) {
555 if (mip6_esm_find_hook)
556 esp = (*mip6_esm_find_hook)(&ip6->ip6_src);
557
558 if ((esp != NULL) && (esp->state >= MIP6_STATE_DEREG)) {
559 if (opt == NULL) {
560 opt = (struct ip6_pktopts *)
561 MALLOC(sizeof(struct ip6_pktopts), M_TEMP, M_WAITOK);
562 if (opt == NULL)
563 return ENOBUFS;
564 bzero(opt, sizeof(struct ip6_pktopts));
565 opt->ip6po_hlim = -1; /* -1 means to use default hop limit */
566 }
567
568 mip6_dest_offset(opt->ip6po_dest2, &off);
569 if ((error = mip6_add_ha(&opt->ip6po_dest2,
570 &off, &ip6->ip6_src, &esp->coa)) != 0)
571 return error;
572
573 /* If the MN initiate the traffic it should add a BU option
574 to the packet if no BUL entry exist and there is a BUL
575 "home registration" entry. */
576 bulp = mip6_bul_find(dst_addr, &esp->home_addr);
577 bulp_hr = mip6_bul_find(NULL, &esp->home_addr);
578 if ((bulp == NULL) && (bulp_hr != NULL)) {
579 /* Create BUL entry and BU option. */
580 bulp = mip6_bul_create(dst_addr, &esp->home_addr,
581 &esp->coa,
582 bulp_hr->lifetime, 0);
583 if (bulp == NULL)
584 return ENOBUFS;
585 mip6_queue_bu(bulp, &esp->home_addr, &esp->coa, 0,
586 bulp_hr->lifetime);
587 }
588 }
589 }
590
591 /* BU, BR and BA should not be sent to link-local, loop-back and
592 multicast addresses. */
593 if (IN6_IS_ADDR_LINKLOCAL(dst_addr) || IN6_IS_ADDR_LOOPBACK(dst_addr) ||
594 IN6_IS_ADDR_MULTICAST(dst_addr)) {
595 *pktopt = opt;
596 return 0;
597 }
598
599 /* If the packet has not been generated completely by MIP6 the
600 output queue is searched. */
601 outp = NULL;
602 if (mip6_config.enable_outq) {
603 for (outp = mip6_outq; outp; outp = outp->next) {
604 if ((outp->flag == NOT_SENT) &&
605 (IN6_ARE_ADDR_EQUAL(&outp->ip6_dst, dst_addr)))
606 break;
607 }
608 }
609 if (outp == NULL) {
610 *pktopt = opt;
611 return 0;
612 }
613
614 /* Destination option (either BU, BR or BA) found in the output list.
615 Add it to the existing destination options. */
616 if (opt == NULL) {
617 opt = (struct ip6_pktopts *)MALLOC(sizeof(struct ip6_pktopts),
618 M_TEMP, M_WAITOK);
619 if (opt == NULL)
620 return ENOBUFS;
621 bzero(opt, sizeof(struct ip6_pktopts));
622 opt->ip6po_hlim = -1; /* -1 means to use default hop limit */
623 }
624
625 mip6_dest_offset(opt->ip6po_dest2, &off);
626 bcopy((caddr_t)outp->opt, (caddr_t)&opttype, 1);
627 if (opttype == IP6OPT_BINDING_UPDATE) {
628 /* Add my Binding Update option to the Destination Header */
629 error = mip6_add_bu(&opt->ip6po_dest2, &off,
630 (struct mip6_opt_bu *)outp->opt,
631 (struct mip6_subbuf *)outp->subopt);
632 if (error)
633 return error;
634 } else if (opttype == IP6OPT_BINDING_ACK) {
635 /* Add my BA option to the Destination Header */
636 error = mip6_add_ba(&opt->ip6po_dest2, &off,
637 (struct mip6_opt_ba *)outp->opt,
638 (struct mip6_subbuf *)outp->subopt);
639 if (error)
640 return error;
641 } else if (opttype == IP6OPT_BINDING_REQ) {
642 /* Add my BR option to the Destination Header */
643 error = mip6_add_br(&opt->ip6po_dest2, &off,
644 (struct mip6_opt_br *)outp->opt,
645 (struct mip6_subbuf *)outp->subopt);
646 if (error)
647 return error;
648 }
649
650 /* Set flag for entry in output queueu to indicate that it has
651 been sent. */
652 outp->flag = SENT;
653 *pktopt = opt;
654 return 0;
655 }
656
657
658
659 /*
660 ##############################################################################
661 #
662 # UTILITY FUNCTIONS
663 # Miscellaneous functions needed for the internal processing of incoming and
664 # outgoing control signals.
665 #
666 ##############################################################################
667 */
668
669 /*
670 ******************************************************************************
671 * Function: mip6_add_rh
672 * Description: Add a Routing Header type 0 to the outgoing packet, if its not
673 * already present, and add the COA for the MN.
674 * If a Routing Header type 0 exist, but contains no data, or the
675 * COA for the MN is missing it is added to the Routing Header.
676 * If the Routing Header is not of type 0 the function returns.
677 * Ret value: 0 OK. Routing Header might have been added
678 * ENOBUFS No memory available
679 * Note: The destination address for the outgoing packet is not changed
680 * since this is taken care of in the ip6_output function.
681 ******************************************************************************
682 */
683 int
684 mip6_add_rh(opt, bcp)
685 struct ip6_pktopts **opt; /* Packet Ext headers, options and data */
686 struct mip6_bc *bcp; /* Binding Cache list entry */
687 {
688 struct ip6_pktopts *opt_local; /* Pkt Ext headers, options & data */
689 struct ip6_rthdr0 *rthdr0; /* Routing header type 0 */
690 struct in6_addr *ip6rt_addr; /* IPv6 routing address(es) */
691 caddr_t ptr; /* Temporary pointer */
692 int ii, len, new_len, idx;
693
694 /* A Multicast address must not appear in a Routing Header. */
695 if (IN6_IS_ADDR_MULTICAST(&bcp->coa))
696 return 0;
697
698 opt_local = *opt;
699 if (opt_local == NULL) {
700 /* No Packet options present at all. Add a Routing Header. */
701 opt_local = (struct ip6_pktopts *)MALLOC(sizeof(struct ip6_pktopts),
702 M_TEMP, M_WAITOK);
703 if (opt_local == NULL)
704 return ENOBUFS;
705 bzero(opt_local, sizeof(struct ip6_pktopts));
706 opt_local->ip6po_hlim = -1; /* -1 means to use default hop limit */
707
708 opt_local->ip6po_rhinfo.ip6po_rhi_rthdr =
709 mip6_create_rh(&bcp->coa, IPPROTO_IP);
710 if(opt_local->ip6po_rhinfo.ip6po_rhi_rthdr == NULL)
711 return ENOBUFS;
712 } else if (opt_local->ip6po_rhinfo.ip6po_rhi_rthdr == NULL) {
713 /* Packet extension header allocated but no RH present, add one. */
714 opt_local->ip6po_rhinfo.ip6po_rhi_rthdr =
715 mip6_create_rh(&bcp->coa, IPPROTO_IP);
716 if(opt_local->ip6po_rhinfo.ip6po_rhi_rthdr == NULL)
717 return ENOBUFS;
718 } else {
719 /* A RH exist. Don't do anything if the type is not 0. */
720 if (opt_local->ip6po_rhinfo.ip6po_rhi_rthdr->ip6r_type !=
721 IPV6_RTHDR_TYPE_0)
722 return 0;
723
724 /* If the outgoing packet contains a BA the Routing Header is
725 correct generated by MIP6. No further action is needed. */
726 if (opt_local->ip6po_dest2 == NULL)
727 return 0;
728
729 len = (opt_local->ip6po_dest2->ip6d_len + 1) << 3;
730 ii = 2;
731 ptr = (caddr_t)opt_local->ip6po_dest2 + 2;
732 while (ii < len) {
733 if (*ptr == IP6OPT_PAD1) {
734 ii += 1;
735 ptr += 1;
736 continue;
737 }
738 if (*ptr == IP6OPT_BINDING_ACK)
739 return 0;
740 ii += *(ptr + 1) + 2;
741 ptr += *(ptr + 1) + 2;
742 }
743
744 /* A routing header exist and the outgoing packet does not include
745 a BA. The routing header has been generated by a user and must
746 be checked. If the last segment is not equal to the MN's COA,
747 add it. */
748 len = opt_local->ip6po_rhinfo.ip6po_rhi_rthdr->ip6r_len;
749 if (len == 0)
750 new_len = 2;
751 else {
752 new_len = len + 2;
753 idx = (len / 2) - 1;
754 rthdr0 = (struct ip6_rthdr0 *)
755 opt_local->ip6po_rhinfo.ip6po_rhi_rthdr;
756 ptr = (caddr_t)rthdr0 + sizeof(struct ip6_rthdr0);
757 ip6rt_addr = (struct in6_addr *)ptr;
758 if (IN6_ARE_ADDR_EQUAL(&bcp->coa, ip6rt_addr + idx))
759 return 0;
760 }
761
762 rthdr0 = (struct ip6_rthdr0 *)
763 MALLOC(sizeof(struct ip6_rthdr0) +
764 (new_len / 2) * sizeof(struct in6_addr), M_TEMP, M_WAITOK);
765 if (rthdr0 == NULL)
766 return ENOBUFS;
767
768 bcopy((caddr_t)opt_local->ip6po_rhinfo.ip6po_rhi_rthdr,
769 (caddr_t)rthdr0, (len + 1) * 8);
770 bcopy((caddr_t)&bcp->coa, (caddr_t)rthdr0 + (len + 1) * 8,
771 sizeof(struct in6_addr));
772 rthdr0->ip6r0_len = new_len;
773 rthdr0->ip6r0_segleft = new_len / 2;
774
775 FREE(opt_local->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
776 opt_local->ip6po_rhinfo.ip6po_rhi_rthdr =
777 (struct ip6_rthdr *)rthdr0;
778 }
779
780 /* Change the IP destination address to the COA for the MN. */
781 *opt = opt_local;
782 return 0;
783 }
784
785
786
787 /*
788 ******************************************************************************
789 * Function: mip6_align
790 * Description: Align the outgoing Destination Header to 8-byte
791 * Ret value: -
792 ******************************************************************************
793 */
794 void
795 mip6_align(dstopt, off)
796 struct ip6_dest *dstopt; /* IPv6 destination options for the packet */
797 int *off; /* Offset from start of Destination Header (byte) */
798 {
799 int rest; /* Rest of modulo division */
800 u_int8_t padlen; /* Number of bytes to pad */
801 u_int8_t padn; /* Number for option type PADN */
802
803 padn = IP6OPT_PADN;
804 rest = *off % 8;
805 if (rest) {
806 padlen = 8 - rest;
807 if (rest == 7) {
808 /* Add a PAD1 option */
809 bzero((caddr_t)dstopt + *off, 1);
810 *off += 1;
811 } else {
812 /* Add a PADN option */
813 bzero((caddr_t)dstopt + *off, padlen);
814 bcopy(&padn, (caddr_t)dstopt + *off, 1);
815 padlen = padlen - 2;
816 bcopy(&padlen, (caddr_t)dstopt + *off + 1, 1);
817 *off += padlen + 2;
818 }
819 }
820 }
821
822
823
824 /*
825 ******************************************************************************
826 * Function: mip6_dest_offset
827 * Description: Calculate offset for new data in the Destination Header.
828 * Additional options will be added beginning at the offset.
829 ******************************************************************************
830 */
831 void
832 mip6_dest_offset(dstopt, off)
833 struct ip6_dest *dstopt; /* IPv6 destination options for the packet */
834 int *off; /* Offset from start of Destination Header (byte) */
835 {
836 int ii; /* Internal counter */
837 u_int8_t opttype; /* Option type found in Destination Header*/
838 u_int8_t optlen; /* Option length incl type and length */
839 u_int32_t len; /* Length of Destination Header in bytes */
840
841 if (dstopt == NULL) {
842 *off = 0;
843 return;
844 }
845
846 len = (dstopt->ip6d_len + 1) << 3;
847 *off = 2;
848
849 for (ii = 2; ii < len;) {
850 bcopy((caddr_t)dstopt + ii, (caddr_t)&opttype, 1);
851 if (opttype == IP6OPT_PAD1) {
852 *off = ii;
853 ii += 1;
854 continue;
855 }
856 bcopy((caddr_t)dstopt + ii + 1, (caddr_t)&optlen, 1);
857 if (opttype == IP6OPT_PADN) {
858 *off = ii;
859 ii += 2 + optlen;
860 } else {
861 ii += 2 + optlen;
862 *off = ii;
863 }
864 }
865 }
866
867
868
869 /*
870 ******************************************************************************
871 * Function: mip6_add_ha
872 * Description: Add Home Address option to the Destination Header. Change the
873 * IPv6 source address to the care-of address of the MN.
874 * Ret value: 0 if OK
875 * Otherwise any appropriate error code
876 ******************************************************************************
877 */
878 int
879 mip6_add_ha(dstopt, off, src_addr, coa)
880 struct ip6_dest **dstopt; /* IPv6 destination options for the packet */
881 int *off; /* Offset from start of Dest Header (byte) */
882 struct in6_addr *src_addr; /* IPv6 header source address */
883 struct in6_addr *coa; /* MN's care-of address */
884 {
885 struct ip6_dest *new_opt; /* Old dest options + Home address option */
886 struct ip6_dest *dest; /* Local variable for destination option */
887 int ii; /* Internal counter */
888 int rest; /* Rest of modulo division */
889 u_int8_t padn; /* Number for option type PADN */
890 u_int8_t opttype; /* Option type */
891 u_int8_t optlen; /* Option length excluding type and length */
892 u_int8_t dstlen; /* destination Header length in 8-bytes */
893 u_int32_t len; /* Length of Destination Header in bytes */
894
895 /* Allocate memory for the Home Address option */
896 dest = *dstopt;
897 if (dest == NULL) {
898 dest = (struct ip6_dest *)MALLOC(sizeof(struct ip6_dest) +
899 sizeof(struct mip6_opt_ha),
900 M_TEMP, M_WAITOK);
901 if (dest == NULL)
902 return ENOBUFS;
903 bzero(dest, sizeof(struct ip6_dest) + sizeof(struct mip6_opt_ha));
904 *off = 2;
905 } else {
906 len = (dest->ip6d_len + 1) << 3;
907 new_opt = (struct ip6_dest *)MALLOC(len +
908 sizeof(struct mip6_opt_ha),
909 M_TEMP, M_WAITOK);
910 if (new_opt == NULL)
911 return ENOBUFS;
912 bzero(new_opt, len + sizeof(struct mip6_opt_ha));
913 bcopy((caddr_t)dest, (caddr_t)new_opt, len);
914 FREE(dest, M_IP6OPT);
915 dest = new_opt;
916 }
917
918 /* Make sure that the offset is correct for adding a Home Address
919 option */
920 padn = IP6OPT_PADN;
921 rest = *off % 4;
922 if (rest == 0) {
923 /* Add a PADN option with length 0 */
924 bzero((caddr_t)dest + *off, 2);
925 bcopy(&padn, (caddr_t)dest + *off, 1);
926 *off += 2;
927 } else if (rest == 1) {
928 /* Add a PAD1 option */
929 bzero((caddr_t)dest + *off, 1);
930 *off += 1;
931 } else if (rest == 3) {
932 /* Add a PADN option with length 1 */
933 bzero((caddr_t)dest + *off, 3);
934 bcopy(&padn, (caddr_t)dest + *off, 1);
935 bcopy(&padn, (caddr_t)dest + *off + 1, 1);
936 *off += 3;
937 }
938
939 /* Add the options in the way they shall be added. */
940 opttype = IP6OPT_HOME_ADDRESS;
941 optlen = IP6OPT_HALEN;
942
943 bcopy(&opttype, (caddr_t)dest + *off, 1);
944 *off += 1;
945 bcopy(&optlen, (caddr_t)dest + *off, 1);
946 *off += 1;
947
948 for (ii = 0; ii < 4; ii++) {
949 bcopy((caddr_t)&src_addr->s6_addr32[ii], (caddr_t)dest + *off, 4);
950 *off += 4;
951 }
952
953 /* Align the Destination Header to 8-byte */
954 mip6_align(dest, off);
955
956 /* Change the total length of the Destination header */
957 dstlen = (*off >> 3) - 1;
958 bcopy(&dstlen, (caddr_t)dest + 1, 1);
959
960 /* Change the IP6 source address to the care-of address */
961 src_addr->s6_addr32[0] = coa->s6_addr32[0];
962 src_addr->s6_addr32[1] = coa->s6_addr32[1];
963 src_addr->s6_addr32[2] = coa->s6_addr32[2];
964 src_addr->s6_addr32[3] = coa->s6_addr32[3];
965 *dstopt = dest;
966 return 0;
967 }
968
969
970
971 /*
972 ******************************************************************************
973 * Function: mip6_add_bu
974 * Description: Copy BU option and sub-option (if present) to a Destination
975 * Header.
976 * Memory in the Destination Header for the BU is created, the
977 * header is aligned to 8-byte alignment and the total length of
978 * the header is updated.
979 * Ret value: 0 if OK
980 * Otherwise any appropriate error code
981 ******************************************************************************
982 */
983 int
984 mip6_add_bu(dstopt, off, optbu, subopt)
985 struct ip6_dest **dstopt; /* IPv6 destination options for the packet */
986 int *off; /* Offset from start of Dest Header (byte) */
987 struct mip6_opt_bu *optbu; /* BU option data */
988 struct mip6_subbuf *subopt; /* BU sub-option data (NULL if not present) */
989 {
990 struct ip6_dest *new_opt; /* Old destination options + BU option */
991 struct ip6_dest *dest; /* Local variable for destination option */
992 u_int8_t padn; /* Number for option type PADN */
993 u_int8_t dstlen; /* Destination Header length in 8-bytes */
994 int offlen; /* Offset for option length in the buffer */
995 int rest; /* Rest of modulo division */
996 int optlen; /* Length of BU option incl sub-options */
997 int tmp16; /* Temporary converting of 2-byte */
998 int tmp32; /* Temporary converting of 4-byte */
999 int len; /* Length of allocated memory */
1000 int after, before;
1001
1002 /* Verify input */
1003 if (optbu == NULL)
1004 return 0;
1005
1006 /* Allocate memory for the BU option and sub-option (if present). */
1007 dest = *dstopt;
1008 if (dest == NULL) {
1009 len = sizeof(struct ip6_dest) + sizeof(struct mip6_opt_bu) + 8;
1010 if (subopt != NULL)
1011 len += subopt->len;
1012
1013 dest = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
1014 if (dest == NULL)
1015 return ENOBUFS;
1016 bzero(dest, len);
1017 *off = 2;
1018 } else {
1019 len = (dest->ip6d_len + 1) << 3;
1020 len += sizeof(struct mip6_opt_bu) + 8;
1021 if (subopt != NULL)
1022 len += subopt->len;
1023
1024 new_opt = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
1025 if (new_opt == NULL)
1026 return ENOBUFS;
1027
1028 bzero(new_opt, len);
1029 bcopy((caddr_t)dest, (caddr_t)new_opt, (dest->ip6d_len + 1) << 3);
1030 FREE(dest, M_IP6OPT);
1031 dest = new_opt;
1032 }
1033
1034 /* Compensate for the alignment requirement. */
1035 padn = IP6OPT_PADN;
1036 rest = *off % 4;
1037 if (rest == 0) {
1038 /* Add a PADN option with length 0 */
1039 bzero((caddr_t)dest + *off, 2);
1040 bcopy(&padn, (caddr_t)dest + *off, 1);
1041 *off += 2;
1042 } else if (rest == 1) {
1043 /* Add a PAD1 option */
1044 bzero((caddr_t)dest + *off, 1);
1045 *off += 1;
1046 } else if (rest == 3) {
1047 /* Add a PADN option with length 1 */
1048 bzero((caddr_t)dest + *off, 3);
1049 bcopy(&padn, (caddr_t)dest + *off, 1);
1050 bcopy(&padn, (caddr_t)dest + *off + 1, 1);
1051 *off += 3;
1052 }
1053 offlen = *off + 1;
1054
1055 /* Reset BU option length in case of retransmission. */
1056 optbu->len = IP6OPT_BULEN;
1057
1058 /* Copy the BU data from the internal structure to the Dest Header */
1059 bcopy((caddr_t)&optbu->type, (caddr_t)dest + *off, sizeof(optbu->type));
1060 *off += sizeof(optbu->type);
1061 bcopy((caddr_t)&optbu->len, (caddr_t)dest + *off, sizeof(optbu->len));
1062 *off += sizeof(optbu->len);
1063 bcopy((caddr_t)&optbu->flags, (caddr_t)dest + *off, sizeof(optbu->flags));
1064 *off += sizeof(optbu->flags);
1065 bcopy((caddr_t)&optbu->prefix_len, (caddr_t)dest + *off,
1066 sizeof(optbu->prefix_len));
1067 *off += sizeof(optbu->prefix_len);
1068 tmp16 = htons(optbu->seqno);
1069 bcopy((caddr_t)&tmp16, (caddr_t)dest + *off, sizeof(optbu->seqno));
1070 *off += sizeof(optbu->seqno);
1071 tmp32 = htonl(optbu->lifetime);
1072 bcopy((caddr_t)&tmp32, (caddr_t)dest + *off, sizeof(optbu->lifetime));
1073 *off += sizeof(optbu->lifetime);
1074
1075 /* If sub-options are present, add them as well. */
1076 optlen = optbu->len;
1077 if (subopt) {
1078 /* Align the Destination Header to 8-byte before sub-options
1079 are added. */
1080 before = *off;
1081 mip6_align(dest, off);
1082 after = *off;
1083 optlen += after - before;
1084
1085 bcopy((caddr_t)subopt->buffer, (caddr_t)dest + *off, subopt->len);
1086 *off += subopt->len;
1087 optlen += subopt->len;
1088 optbu->len += subopt->len;
1089 }
1090
1091 /* Make sure that the option length is correct. */
1092 bcopy((caddr_t)&optlen, (caddr_t)dest + offlen, 1);
1093
1094 /* Align the Destination Header to 8-byte */
1095 mip6_align(dest, off);
1096
1097 /* Change the total length of the Destination header */
1098 dstlen = (*off >> 3) - 1;
1099 bcopy(&dstlen, (caddr_t)dest + 1, 1);
1100 *dstopt = dest;
1101 return 0;
1102 }
1103
1104
1105
1106 /*
1107 ******************************************************************************
1108 * Function: mip6_add_ba
1109 * Description: Copy BA option and sub-option (if present) to a Destination
1110 * Header.
1111 * Memory in the Destination Header for the BU is created, the
1112 * header is aligned to 8-byte alignment and the total length of
1113 * the header is updated.
1114 * Ret value: 0 if OK
1115 * Otherwise any appropriate error code
1116 ******************************************************************************
1117 */
1118 int
1119 mip6_add_ba(dstopt, off, optba, subopt)
1120 struct ip6_dest **dstopt; /* IPv6 dest options for the packet */
1121 int *off; /* Offset from start of dest Header (byte) */
1122 struct mip6_opt_ba *optba; /* BA option data */
1123 struct mip6_subbuf *subopt; /* BA sub-option data (NULL if not present) */
1124 {
1125 struct ip6_dest *new_opt; /* Old destination options + BA option */
1126 struct ip6_dest *dest; /* Local variable for destination option */
1127 u_int8_t padn; /* Number for option type PADN */
1128 u_int8_t dstlen; /* Destination Header length in 8-bytes */
1129 int offlen; /* Offset for option length in the buffer */
1130 int optlen; /* Length of BA option incl sub-options */
1131 int rest; /* Rest of modulo division */
1132 int tmp16; /* Temporary converting of 2-byte */
1133 int tmp32; /* Temporary converting of 4-byte */
1134 int len; /* Length of allocated memory */
1135 int after, before;
1136
1137 /* Verify input */
1138 if (optba == NULL)
1139 return 0;
1140
1141 /* Allocate memory for the BA option and sub-option (if present). */
1142 dest = *dstopt;
1143 if (dest == NULL) {
1144 len = sizeof(struct ip6_dest) + sizeof(struct mip6_opt_ba) + 8;
1145 if (subopt != NULL)
1146 len += subopt->len;
1147
1148 dest = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
1149 if (dest == NULL)
1150 return ENOBUFS;
1151 bzero(dest, len);
1152 *off = 2;
1153 } else {
1154 len = (dest->ip6d_len + 1) << 3;
1155 len += sizeof(struct mip6_opt_ba) + 8;
1156 if (subopt != NULL)
1157 len += subopt->len;
1158
1159 new_opt = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
1160 if (new_opt == NULL)
1161 return ENOBUFS;
1162 bzero(new_opt, len);
1163 bcopy((caddr_t)dest, (caddr_t)new_opt, (dest->ip6d_len + 1) << 3);
1164 FREE(dest, M_IP6OPT);
1165 dest = new_opt;
1166 }
1167
1168 /* Compensate for the alignment requirement. */
1169 padn = IP6OPT_PADN;
1170 rest = *off % 4;
1171 if (rest == 1) {
1172 /* Add a PADN option with length 0 */
1173 bzero((caddr_t)dest + *off, 2);
1174 bcopy(&padn, (caddr_t)dest + *off, 1);
1175 *off += 2;
1176 } else if (rest == 2) {
1177 /* Add a PAD1 option */
1178 bzero((caddr_t)dest + *off, 1);
1179 *off += 1;
1180 } else if (rest == 0) {
1181 /* Add a PADN option with length 1 */
1182 bzero((caddr_t)dest + *off, 3);
1183 bcopy(&padn, (caddr_t)dest + *off, 1);
1184 bcopy(&padn, (caddr_t)dest + *off + 1, 1);
1185 *off += 3;
1186 }
1187 offlen = *off + 1;
1188
1189 /* Copy the BA data from the internal structure to mbuf */
1190 bcopy((caddr_t)&optba->type, (caddr_t)dest + *off, sizeof(optba->type));
1191 *off += sizeof(optba->type);
1192 bcopy((caddr_t)&optba->len, (caddr_t)dest + *off, sizeof(optba->len));
1193 *off += sizeof(optba->len);
1194 bcopy((caddr_t)&optba->status, (caddr_t)dest + *off,
1195 sizeof(optba->status));
1196 *off += sizeof(optba->status);
1197 tmp16 = htons(optba->seqno);
1198 bcopy((caddr_t)&tmp16, (caddr_t)dest + *off, sizeof(optba->seqno));
1199 *off += sizeof(optba->seqno);
1200 tmp32 = htonl(optba->lifetime);
1201 bcopy((caddr_t)&tmp32, (caddr_t)dest + *off, sizeof(optba->lifetime));
1202 *off += sizeof(optba->lifetime);
1203 tmp32 = htonl(optba->refresh);
1204 bcopy((caddr_t)&tmp32, (caddr_t)dest + *off, sizeof(optba->refresh));
1205 *off += sizeof(optba->refresh);
1206
1207 /* If sub-options are present, add them as well. */
1208 optlen = IP6OPT_BALEN;
1209 if (subopt) {
1210 /* Align the Destination Header to 8-byte before sub-options
1211 are added. */
1212 before = *off;
1213 mip6_align(dest, off);
1214 after = *off;
1215 optlen += after - before;
1216
1217 bcopy((caddr_t)subopt->buffer, (caddr_t)dest + *off, subopt->len);
1218 *off += subopt->len;
1219 optlen += subopt->len;
1220 optba->len += subopt->len;
1221 }
1222
1223 /* Make sure that the option length is correct. */
1224 bcopy((caddr_t)&optlen, (caddr_t)dest + offlen, 1);
1225
1226 /* Align the Destination Header to 8-byte */
1227 mip6_align(dest, off);
1228
1229 /* Change the total length of the Destination header */
1230 dstlen = (*off >> 3) - 1;
1231 bcopy(&dstlen, (caddr_t)dest + 1, 1);
1232 *dstopt = dest;
1233 return 0;
1234 }
1235
1236
1237
1238 /*
1239 ******************************************************************************
1240 * Function: mip6_add_br
1241 * Description: Copy BR option and sub-option (if present) to a Destination
1242 * Header.
1243 * Memory in the Destination Header for the BU is created, the
1244 * header is aligned to 8-byte alignment and the total length of
1245 * the header is updated.
1246 * Ret value: 0 if OK
1247 * Otherwise any appropriate error code
1248 ******************************************************************************
1249 */
1250 int
1251 mip6_add_br(dstopt, off, optbr, subopt)
1252 struct ip6_dest **dstopt; /* IPv6 destination options for the packet */
1253 int *off; /* Offset from start of Dest Header (byte) */
1254 struct mip6_opt_br *optbr; /* BR option data */
1255 struct mip6_subbuf *subopt; /* BR sub-option data (NULL if not present) */
1256 {
1257 struct ip6_dest *new_opt; /* Old destination options + BU option */
1258 struct ip6_dest *dest; /* Local variable for destination option */
1259 u_int8_t dstlen; /* Destination Header length in 8-bytes */
1260 int offlen; /* Offset for option length in the buffer */
1261 int rest; /* Rest of modulo division */
1262 int optlen; /* Length of BR option incl sub-options */
1263 int len; /* Length of allocated memory */
1264 int after, before;
1265
1266 /* Verify input */
1267 if (optbr == NULL)
1268 return 0;
1269
1270 /* Allocate memory for the BR option and sub-option (if present). */
1271 dest = *dstopt;
1272 if (dest == NULL) {
1273 len = sizeof(struct ip6_dest) + sizeof(struct mip6_opt_br) + 8;
1274 if (subopt != NULL)
1275 len += subopt->len;
1276
1277 dest = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
1278 if (dest == NULL)
1279 return ENOBUFS;
1280
1281 bzero(dest, len);
1282 *off = 2;
1283 } else {
1284 len = (dest->ip6d_len + 1) << 3;
1285 len += sizeof(struct mip6_opt_br) + 8;
1286 if (subopt != NULL)
1287 len += subopt->len;
1288
1289 new_opt = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
1290 if (new_opt == NULL)
1291 return ENOBUFS;
1292
1293 bzero(new_opt, len);
1294 bcopy((caddr_t)dest, (caddr_t)new_opt, (dest->ip6d_len + 1) << 3);
1295 FREE(dest, M_IP6OPT);
1296 dest = new_opt;
1297 }
1298
1299 /* Compensate for the alignment requirement. */
1300 rest = *off % 4;
1301 if ((rest == 1) || (rest == 3)) {
1302 /* Add a PAD1 option */
1303 bzero((caddr_t)dest + *off, 1);
1304 *off += 1;
1305 }
1306 offlen = *off +1;
1307
1308 /* Copy the BR data from the internal structure to mbuf */
1309 bcopy((caddr_t)&optbr->type, (caddr_t)dest + *off, sizeof(optbr->type));
1310 *off += sizeof(optbr->type);
1311 bcopy((caddr_t)&optbr->len, (caddr_t)dest + *off, sizeof(optbr->len));
1312 *off += sizeof(optbr->len);
1313
1314
1315 /* If sub-options are present, add them as well. */
1316 optlen = IP6OPT_BRLEN;
1317 if (subopt) {
1318 /* Align the Destination Header to 8-byte before sub-options
1319 are added. */
1320 before = *off;
1321 mip6_align(dest, off);
1322 after = *off;
1323 optlen += after - before;
1324
1325 bcopy((caddr_t)subopt->buffer, (caddr_t)dest + *off, subopt->len);
1326 *off += subopt->len;
1327 optlen += subopt->len;
1328 optbr->len += subopt->len;
1329 }
1330
1331 /* Make sure that the option length is correct. */
1332 bcopy((caddr_t)&optlen, (caddr_t)dest + offlen, 1);
1333
1334 /* Align the Destination Header to 8-byte */
1335 mip6_align(dest, off);
1336
1337 /* Change the total length of the Destination header */
1338 dstlen = (*off >> 3) - 1;
1339 bcopy(&dstlen, (caddr_t)dest + 1, 1);
1340 *dstopt = dest;
1341 return 0;
1342 }
1343
1344
1345
1346 /*
1347 ******************************************************************************
1348 * Function: mip6_store_subopt
1349 * Description: Store a sub-option in a buffer. The buffer must be allocated
1350 * by the calling function and big enough to hold all the sub-
1351 * options that may be added to an option (BU, BR or BA).
1352 * Alignement requirement for the different sub-options are taken
1353 * care of before its added to the buffer.
1354 * Ret value: 0 if OK. Otherwise 1
1355 ******************************************************************************
1356 */
1357 int
1358 mip6_store_subopt(subbuf, subopt)
1359 struct mip6_subbuf **subbuf; /* Buffert containing sub-options */
1360 caddr_t subopt; /* TLV coded sub-option */
1361 {
1362 struct mip6_subopt_id *uid;
1363 struct mip6_subopt_hal *hal;
1364 struct mip6_subopt_coa *altcoa;
1365 struct mip6_subbuf *buf;
1366 u_int8_t pad1, padn;
1367 u_int16_t tmp16;
1368 int rest, no, ii, padlen;
1369
1370 /* Make sure that a sub-option is present. */
1371 if (subopt == NULL)
1372 return 0;
1373
1374 /* Allocate memory for buffer if not already allocated. */
1375 buf = *subbuf;
1376 if (buf == NULL) {
1377 buf = (struct mip6_subbuf *)MALLOC(sizeof(struct mip6_subbuf),
1378 M_TEMP, M_WAITOK);
1379 if (buf == NULL)
1380 return 1;
1381 bzero(buf, sizeof(struct mip6_subbuf));
1382 }
1383
1384 /* Find offset in the current buffer */
1385 padn = IP6OPT_PADN;
1386 pad1 = IP6OPT_PAD1;
1387
1388 switch (*subopt) {
1389 case IP6SUBOPT_UNIQUEID:
1390 /* Make sure that the length is OK */
1391 uid = (struct mip6_subopt_id *)subopt;
1392 if (uid->len != IP6OPT_UIDLEN)
1393 return 1;
1394
1395 /* Compensate for the alignment requirement. */
1396 rest = buf->len % 2;
1397 if (rest == 1) {
1398 bcopy(&pad1, (caddr_t)buf->buffer + buf->len, 1);
1399 buf->len += 1;
1400 }
1401
1402 /* Copy the sub-option to the buffer. */
1403 bcopy(&uid->type, (caddr_t)buf->buffer + buf->len,
1404 sizeof(uid->type));
1405 buf->len += sizeof(uid->type);
1406
1407 bcopy(&uid->len, (caddr_t)buf->buffer + buf->len,
1408 sizeof(uid->len));
1409 buf->len += sizeof(uid->len);
1410
1411 tmp16 = htons(uid->id);
1412 bcopy(&tmp16, (caddr_t)buf->buffer + buf->len, sizeof(tmp16));
1413 buf->len += sizeof(tmp16);
1414 break;
1415 case IP6SUBOPT_HALIST:
1416 /* Make sure that the length is OK */
1417 hal = (struct mip6_subopt_hal *)subopt;
1418 if (hal->len % IP6OPT_HALISTLEN)
1419 return 1;
1420
1421 /* Compensate for the alignment requirement. */
1422 rest = buf->len % 8;
1423 if (rest > 3) {
1424 padlen = rest - 4;
1425 bcopy(&padn, (caddr_t)buf->buffer + buf->len, 1);
1426 buf->len += 1;
1427 bcopy(&padlen, (caddr_t)buf->buffer + buf->len, 1);
1428 buf->len += 1;
1429 bzero((caddr_t)buf->buffer + buf->len, padlen);
1430 buf->len += padlen;
1431 } else if (rest == 3) {
1432 bcopy(&pad1, (caddr_t)buf->buffer + buf->len, 1);
1433 buf->len += 1;
1434 } else if (rest <= 1) {
1435 padlen = rest + 4;
1436 bcopy(&padn, (caddr_t)buf->buffer + buf->len, 1);
1437 buf->len += 1;
1438 bcopy(&padlen, (caddr_t)buf->buffer + buf->len, 1);
1439 buf->len += 1;
1440 bzero((caddr_t)buf->buffer + buf->len, padlen);
1441 buf->len += padlen;
1442 }
1443
1444 /* Copy the sub-option to the buffer. */
1445 bcopy(&hal->type, (caddr_t)buf->buffer + buf->len,
1446 sizeof(hal->type));
1447 buf->len += sizeof(hal->type);
1448
1449 bcopy(&hal->len, (caddr_t)buf->buffer + buf->len,
1450 sizeof(hal->len));
1451 buf->len += sizeof(hal->len);
1452
1453 /* Loop over the addresses */
1454 no = hal->len / IP6OPT_HALISTLEN;
1455 for (ii = 0; ii < no; ii++) {
1456 bcopy(&hal->halist[ii], (caddr_t)buf->buffer + buf->len,
1457 sizeof(hal->halist));
1458 buf->len += sizeof(hal->halist);
1459 }
1460 break;
1461 case IP6SUBOPT_ALTCOA:
1462 /* Make sure that the length is OK */
1463 altcoa = (struct mip6_subopt_coa *)subopt;
1464 if (altcoa->len % IP6OPT_COALEN)
1465 return 1;
1466
1467 /* Compensate for the alignment requirement. */
1468 rest = buf->len % 8;
1469 if (rest > 3) {
1470 padlen = rest - 4;
1471 bcopy(&padn, (caddr_t)buf->buffer + buf->len, 1);
1472 buf->len += 1;
1473 bcopy(&padlen, (caddr_t)buf->buffer + buf->len, 1);
1474 buf->len += 1;
1475 bzero((caddr_t)buf->buffer + buf->len, padlen);
1476 buf->len += padlen;
1477 } else if (rest == 3) {
1478 bcopy(&pad1, (caddr_t)buf->buffer + buf->len, 1);
1479 buf->len += 1;
1480 } else if (rest <= 1) {
1481 padlen = rest + 4;
1482 bcopy(&padn, (caddr_t)buf->buffer + buf->len, 1);
1483 buf->len += 1;
1484 bcopy(&padlen, (caddr_t)buf->buffer + buf->len, 1);
1485 buf->len += 1;
1486 bzero((caddr_t)buf->buffer + buf->len, padlen);
1487 buf->len += padlen;
1488 }
1489
1490 /* Copy the sub-option to the buffer. */
1491 bcopy(&altcoa->type, (caddr_t)buf->buffer + buf->len,
1492 sizeof(altcoa->type));
1493 buf->len += sizeof(altcoa->type);
1494
1495 bcopy(&altcoa->len, (caddr_t)buf->buffer + buf->len,
1496 sizeof(altcoa->len));
1497 buf->len += sizeof(altcoa->len);
1498
1499 bcopy(&altcoa->coa, (caddr_t)buf->buffer + buf->len,
1500 sizeof(altcoa->coa));
1501 buf->len += sizeof(altcoa->coa);
1502 break;
1503 default:
1504 }
1505 *subbuf = buf;
1506 return 0;
1507 }