]>
Commit | Line | Data |
---|---|---|
1c79356b A |
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 | } |