#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
#include "opt_inet.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/mip6.h>
#include <netinet6/mip6_common.h>
void (*mip6_icmp6_output_hook)(struct mbuf *) = 0;
struct mip6_esm * (*mip6_esm_find_hook)(struct in6_addr *) = 0;
struct mip6_indata *mip6_inp = NULL;
struct mip6_output *mip6_outq = NULL;
int
mip6_new_packet(m)
struct mbuf *m;
{
if (mip6_inp != NULL) {
if (mip6_inp->bu_opt != NULL)
FREE(mip6_inp->bu_opt, M_TEMP);
if (mip6_inp->ba_opt != NULL)
FREE(mip6_inp->ba_opt, M_TEMP);
if (mip6_inp->br_opt != NULL)
FREE(mip6_inp->br_opt, M_TEMP);
if (mip6_inp->ha_opt != NULL)
FREE(mip6_inp->ha_opt, M_TEMP);
if (mip6_inp->uid != NULL)
FREE(mip6_inp->uid, M_TEMP);
if (mip6_inp->coa != NULL)
FREE(mip6_inp->coa, M_TEMP);
if (mip6_inp->hal != NULL)
FREE(mip6_inp->hal, M_TEMP);
FREE(mip6_inp, M_TEMP);
mip6_inp = NULL;
}
mip6_inp = (struct mip6_indata *)
MALLOC(sizeof(struct mip6_indata), M_TEMP, M_WAITOK);
if (mip6_inp == NULL)
panic("%s: We should not come here !!!!", __FUNCTION__);
bzero(mip6_inp, sizeof(struct mip6_indata));
return 0;
}
int
mip6_store_dstopt_pre(m, opt, off, dstlen)
struct mbuf *m;
u_int8_t *opt;
u_int8_t off;
u_int8_t dstlen;
{
u_int8_t type;
type = *opt;
if (type == IP6OPT_BINDING_UPDATE) {
if (dstlen < IP6OPT_BUMINLEN) {
ip6stat.ip6s_toosmall++;
return IPPROTO_DONE;
}
if (mip6_store_dstopt(m, opt, off-dstlen) != 0)
return IPPROTO_DONE;
} else if (type == IP6OPT_BINDING_ACK) {
if (dstlen < IP6OPT_BAMINLEN) {
ip6stat.ip6s_toosmall++;
return IPPROTO_DONE;
}
if (mip6_store_dstopt(m, opt, off-dstlen) != 0)
return IPPROTO_DONE;
} else if (type == IP6OPT_BINDING_REQ) {
if (dstlen < IP6OPT_BRMINLEN) {
ip6stat.ip6s_toosmall++;
return IPPROTO_DONE;
}
if (mip6_store_dstopt(m, opt, off-dstlen) != 0)
return IPPROTO_DONE;
} else if (type == IP6OPT_HOME_ADDRESS) {
if (dstlen < IP6OPT_HAMINLEN) {
ip6stat.ip6s_toosmall++;
return IPPROTO_DONE;
}
if (mip6_store_dstopt(m, opt, off-dstlen) != 0)
return IPPROTO_DONE;
}
return 0;
}
int
mip6_store_dstopt(mp, opt, optoff)
struct mbuf *mp;
u_int8_t *opt;
u_int8_t optoff;
{
struct mip6_opt_bu *bu_opt;
struct mip6_opt_ba *ba_opt;
struct mip6_opt_br *br_opt;
struct mip6_opt_ha *ha_opt;
int tmplen;
int totlen;
int error;
switch (*opt) {
case IP6OPT_BINDING_UPDATE:
mip6_inp->bu_opt = (struct mip6_opt_bu *)
MALLOC(sizeof(struct mip6_opt_bu), M_TEMP, M_WAITOK);
if (mip6_inp->bu_opt == NULL)
return ENOBUFS;
bzero(mip6_inp->bu_opt, sizeof(struct mip6_opt_bu));
bu_opt = mip6_inp->bu_opt;
m_copydata(mp, optoff, sizeof(bu_opt->type),
(caddr_t)&bu_opt->type);
tmplen = sizeof(bu_opt->type);
m_copydata(mp, optoff + tmplen, sizeof(bu_opt->len),
(caddr_t)&bu_opt->len);
tmplen += sizeof(bu_opt->len);
m_copydata(mp, optoff + tmplen, sizeof(bu_opt->flags),
(caddr_t)&bu_opt->flags);
tmplen += sizeof(bu_opt->flags);
m_copydata(mp, optoff + tmplen, sizeof(bu_opt->prefix_len),
(caddr_t)&bu_opt->prefix_len);
tmplen += sizeof(bu_opt->prefix_len);
m_copydata(mp, optoff + tmplen, sizeof(bu_opt->seqno),
(caddr_t)&bu_opt->seqno);
tmplen += sizeof(bu_opt->seqno);
m_copydata(mp, optoff + tmplen, sizeof(bu_opt->lifetime),
(caddr_t)&bu_opt->lifetime);
tmplen += sizeof(bu_opt->lifetime);
bu_opt->seqno = ntohs(bu_opt->seqno);
bu_opt->lifetime = ntohl(bu_opt->lifetime);
mip6_inp->optflag |= MIP6_DSTOPT_BU;
if (bu_opt->len > IP6OPT_BULEN) {
totlen = bu_opt->len + 2;
error = mip6_store_dstsubopt(mp, opt, optoff, totlen, tmplen);
if (error)
return error;
}
break;
case IP6OPT_BINDING_ACK:
mip6_inp->ba_opt = (struct mip6_opt_ba *)
MALLOC(sizeof(struct mip6_opt_ba), M_TEMP, M_WAITOK);
if (mip6_inp->ba_opt == NULL)
return ENOBUFS;
bzero(mip6_inp->ba_opt, sizeof(struct mip6_opt_ba));
ba_opt = mip6_inp->ba_opt;
m_copydata(mp, optoff, sizeof(ba_opt->type),
(caddr_t)&ba_opt->type);
tmplen = sizeof(ba_opt->type);
m_copydata(mp, optoff + tmplen, sizeof(ba_opt->len),
(caddr_t)&ba_opt->len);
tmplen += sizeof(ba_opt->len);
m_copydata(mp, optoff + tmplen, sizeof(ba_opt->status),
(caddr_t)&ba_opt->status);
tmplen += sizeof(ba_opt->status);
m_copydata(mp, optoff + tmplen, sizeof(ba_opt->seqno),
(caddr_t)&ba_opt->seqno);
tmplen += sizeof(ba_opt->seqno);
m_copydata(mp, optoff + tmplen, sizeof(ba_opt->lifetime),
(caddr_t)&ba_opt->lifetime);
tmplen += sizeof(ba_opt->lifetime);
m_copydata(mp, optoff + tmplen, sizeof(ba_opt->refresh),
(caddr_t)&ba_opt->refresh);
tmplen += sizeof(ba_opt->refresh);
ba_opt->seqno = ntohs(ba_opt->seqno);
ba_opt->lifetime = ntohl(ba_opt->lifetime);
ba_opt->refresh = ntohl(ba_opt->refresh);
mip6_inp->optflag |= MIP6_DSTOPT_BA;
if (ba_opt->len > IP6OPT_BALEN) {
totlen = ba_opt->len + 2;
error = mip6_store_dstsubopt(mp, opt, optoff, totlen, tmplen);
if (error)
return error;
}
break;
case IP6OPT_BINDING_REQ:
mip6_inp->br_opt = (struct mip6_opt_br *)
MALLOC(sizeof(struct mip6_opt_br), M_TEMP, M_WAITOK);
if (mip6_inp->br_opt == NULL)
return ENOBUFS;
bzero(mip6_inp->br_opt, sizeof(struct mip6_opt_br));
br_opt = mip6_inp->br_opt;
m_copydata(mp, optoff, sizeof(br_opt->type),
(caddr_t)&br_opt->type);
tmplen = sizeof(br_opt->type);
m_copydata(mp, optoff + tmplen, sizeof(br_opt->len),
(caddr_t)&br_opt->len);
tmplen += sizeof(br_opt->len);
mip6_inp->optflag |= MIP6_DSTOPT_BR;
if (br_opt->len > IP6OPT_BRLEN) {
totlen = br_opt->len + 2;
error = mip6_store_dstsubopt(mp, opt, optoff, totlen, tmplen);
if (error)
return error;
}
break;
case IP6OPT_HOME_ADDRESS:
mip6_inp->ha_opt = (struct mip6_opt_ha *)
MALLOC(sizeof(struct mip6_opt_ha), M_TEMP, M_WAITOK);
if (mip6_inp->ha_opt == NULL)
return ENOBUFS;
bzero(mip6_inp->ha_opt, sizeof(struct mip6_opt_ha));
ha_opt = mip6_inp->ha_opt;
m_copydata(mp, optoff, sizeof(ha_opt->type),
(caddr_t)&ha_opt->type);
tmplen = sizeof(ha_opt->type);
m_copydata(mp, optoff + tmplen, sizeof(ha_opt->len),
(caddr_t)&ha_opt->len);
tmplen += sizeof(ha_opt->len);
m_copydata(mp, optoff + tmplen, sizeof(ha_opt->home_addr),
(caddr_t)&ha_opt->home_addr);
tmplen += sizeof(ha_opt->home_addr);
mip6_inp->optflag |= MIP6_DSTOPT_HA;
break;
default:
}
return 0;
}
int
mip6_store_dstsubopt(mp, opt, optoff, totlen, tmplen)
struct mbuf *mp;
u_int8_t *opt;
u_int8_t optoff;
int totlen;
int tmplen;
{
struct mip6_subopt_hal *hal;
struct mip6_subopt_coa *coa;
int ii, len;
while (tmplen < totlen) {
switch (*(opt + tmplen)) {
case IP6OPT_PAD1:
tmplen += 1;
break;
case IP6OPT_PADN:
tmplen += *(opt + tmplen + 1) + 2;
break;
case IP6SUBOPT_UNIQUEID:
if (*(opt + tmplen + 1) != IP6OPT_UIDLEN) {
MIP6_FREEINDATA;
return EIO;
}
mip6_inp->uid = (struct mip6_subopt_id *)
MALLOC(sizeof(struct mip6_subopt_id), M_TEMP, M_WAITOK);
if (mip6_inp->uid == NULL)
return ENOBUFS;
bzero(mip6_inp->uid, sizeof(struct mip6_subopt_id));
m_copydata(mp, optoff + tmplen, sizeof(struct mip6_subopt_id),
(caddr_t)mip6_inp->uid);
tmplen += sizeof(struct mip6_subopt_id);
mip6_inp->uid->id = ntohs(mip6_inp->uid->id);
mip6_inp->optflag |= MIP6_DSTOPT_UID;
break;
case IP6SUBOPT_HALIST:
if (*(opt + tmplen + 1) % IP6OPT_HALISTLEN) {
MIP6_FREEINDATA;
return EIO;
}
len = *(opt + tmplen +1) / IP6OPT_HALISTLEN;
mip6_inp->hal = (struct mip6_subopt_hal *)
MALLOC(sizeof(struct mip6_subopt_hal) +
(len - 1) * sizeof(struct in6_addr),
M_TEMP, M_WAITOK);
if (mip6_inp->hal == NULL) {
MIP6_FREEINDATA;
return ENOMEM;
}
hal = mip6_inp->hal;
m_copydata(mp, optoff + tmplen, sizeof(hal->type),
(caddr_t)&hal->type);
tmplen += sizeof(hal->type);
m_copydata(mp, optoff + tmplen, sizeof(hal->len),
(caddr_t)&hal->len);
tmplen += sizeof(hal->len);
for (ii = 0; ii < len; ii++) {
m_copydata(mp, optoff, tmplen, (caddr_t)&hal->halist[ii]);
tmplen += sizeof(struct in6_addr);
}
mip6_inp->optflag |= MIP6_DSTOPT_HAL;
break;
case IP6SUBOPT_ALTCOA:
if (*(opt + tmplen + 1) != IP6OPT_COALEN) {
MIP6_FREEINDATA;
return EIO;
}
mip6_inp->coa = (struct mip6_subopt_coa *)
MALLOC(sizeof(struct mip6_subopt_coa), M_TEMP, M_WAITOK);
if (mip6_inp->coa == NULL)
return ENOBUFS;
bzero(mip6_inp->coa, sizeof(struct mip6_subopt_coa));
coa = mip6_inp->coa;
m_copydata(mp, optoff + tmplen, sizeof(coa->type),
(caddr_t)&coa->type);
tmplen += sizeof(coa->type);
m_copydata(mp, optoff + tmplen, sizeof(coa->len),
(caddr_t)&coa->len);
tmplen += sizeof(coa->len);
m_copydata(mp, optoff + tmplen, sizeof(coa->coa),
(caddr_t)&coa->coa);
tmplen += sizeof(coa->coa);
mip6_inp->optflag |= MIP6_DSTOPT_COA;
break;
default:
tmplen += *(opt + tmplen + 1) + 2;
}
}
return 0;
}
int
mip6_output(m, pktopt)
struct mbuf *m;
struct ip6_pktopts **pktopt;
{
struct ip6_pktopts *opt;
struct mip6_output *outp;
struct mip6_esm *esp;
struct ip6_hdr *ip6;
struct mip6_bc *bcp;
struct mip6_bul *bulp;
struct mip6_bul *bulp_hr;
struct in6_addr *dst_addr;
int error;
int off;
u_int8_t opttype;
ip6 = mtod(m, struct ip6_hdr *);
opt = *pktopt;
if (MIP6_IS_HA_ACTIVE) {
if (ip6->ip6_nxt == IPPROTO_ICMPV6)
if (mip6_icmp6_output_hook) (*mip6_icmp6_output_hook)(m);
}
dst_addr = &ip6->ip6_dst;
bcp = mip6_bc_find(&ip6->ip6_dst);
if (bcp != NULL) {
dst_addr = &bcp->home_addr;
if ((error = mip6_add_rh(&opt, bcp)) != 0)
return error;
}
esp = NULL;
if (MIP6_IS_MN_ACTIVE) {
if (mip6_esm_find_hook)
esp = (*mip6_esm_find_hook)(&ip6->ip6_src);
if ((esp != NULL) && (esp->state >= MIP6_STATE_DEREG)) {
if (opt == NULL) {
opt = (struct ip6_pktopts *)
MALLOC(sizeof(struct ip6_pktopts), M_TEMP, M_WAITOK);
if (opt == NULL)
return ENOBUFS;
bzero(opt, sizeof(struct ip6_pktopts));
opt->ip6po_hlim = -1;
}
mip6_dest_offset(opt->ip6po_dest2, &off);
if ((error = mip6_add_ha(&opt->ip6po_dest2,
&off, &ip6->ip6_src, &esp->coa)) != 0)
return error;
bulp = mip6_bul_find(dst_addr, &esp->home_addr);
bulp_hr = mip6_bul_find(NULL, &esp->home_addr);
if ((bulp == NULL) && (bulp_hr != NULL)) {
bulp = mip6_bul_create(dst_addr, &esp->home_addr,
&esp->coa,
bulp_hr->lifetime, 0);
if (bulp == NULL)
return ENOBUFS;
mip6_queue_bu(bulp, &esp->home_addr, &esp->coa, 0,
bulp_hr->lifetime);
}
}
}
if (IN6_IS_ADDR_LINKLOCAL(dst_addr) || IN6_IS_ADDR_LOOPBACK(dst_addr) ||
IN6_IS_ADDR_MULTICAST(dst_addr)) {
*pktopt = opt;
return 0;
}
outp = NULL;
if (mip6_config.enable_outq) {
for (outp = mip6_outq; outp; outp = outp->next) {
if ((outp->flag == NOT_SENT) &&
(IN6_ARE_ADDR_EQUAL(&outp->ip6_dst, dst_addr)))
break;
}
}
if (outp == NULL) {
*pktopt = opt;
return 0;
}
if (opt == NULL) {
opt = (struct ip6_pktopts *)MALLOC(sizeof(struct ip6_pktopts),
M_TEMP, M_WAITOK);
if (opt == NULL)
return ENOBUFS;
bzero(opt, sizeof(struct ip6_pktopts));
opt->ip6po_hlim = -1;
}
mip6_dest_offset(opt->ip6po_dest2, &off);
bcopy((caddr_t)outp->opt, (caddr_t)&opttype, 1);
if (opttype == IP6OPT_BINDING_UPDATE) {
error = mip6_add_bu(&opt->ip6po_dest2, &off,
(struct mip6_opt_bu *)outp->opt,
(struct mip6_subbuf *)outp->subopt);
if (error)
return error;
} else if (opttype == IP6OPT_BINDING_ACK) {
error = mip6_add_ba(&opt->ip6po_dest2, &off,
(struct mip6_opt_ba *)outp->opt,
(struct mip6_subbuf *)outp->subopt);
if (error)
return error;
} else if (opttype == IP6OPT_BINDING_REQ) {
error = mip6_add_br(&opt->ip6po_dest2, &off,
(struct mip6_opt_br *)outp->opt,
(struct mip6_subbuf *)outp->subopt);
if (error)
return error;
}
outp->flag = SENT;
*pktopt = opt;
return 0;
}
int
mip6_add_rh(opt, bcp)
struct ip6_pktopts **opt;
struct mip6_bc *bcp;
{
struct ip6_pktopts *opt_local;
struct ip6_rthdr0 *rthdr0;
struct in6_addr *ip6rt_addr;
caddr_t ptr;
int ii, len, new_len, idx;
if (IN6_IS_ADDR_MULTICAST(&bcp->coa))
return 0;
opt_local = *opt;
if (opt_local == NULL) {
opt_local = (struct ip6_pktopts *)MALLOC(sizeof(struct ip6_pktopts),
M_TEMP, M_WAITOK);
if (opt_local == NULL)
return ENOBUFS;
bzero(opt_local, sizeof(struct ip6_pktopts));
opt_local->ip6po_hlim = -1;
opt_local->ip6po_rhinfo.ip6po_rhi_rthdr =
mip6_create_rh(&bcp->coa, IPPROTO_IP);
if(opt_local->ip6po_rhinfo.ip6po_rhi_rthdr == NULL)
return ENOBUFS;
} else if (opt_local->ip6po_rhinfo.ip6po_rhi_rthdr == NULL) {
opt_local->ip6po_rhinfo.ip6po_rhi_rthdr =
mip6_create_rh(&bcp->coa, IPPROTO_IP);
if(opt_local->ip6po_rhinfo.ip6po_rhi_rthdr == NULL)
return ENOBUFS;
} else {
if (opt_local->ip6po_rhinfo.ip6po_rhi_rthdr->ip6r_type !=
IPV6_RTHDR_TYPE_0)
return 0;
if (opt_local->ip6po_dest2 == NULL)
return 0;
len = (opt_local->ip6po_dest2->ip6d_len + 1) << 3;
ii = 2;
ptr = (caddr_t)opt_local->ip6po_dest2 + 2;
while (ii < len) {
if (*ptr == IP6OPT_PAD1) {
ii += 1;
ptr += 1;
continue;
}
if (*ptr == IP6OPT_BINDING_ACK)
return 0;
ii += *(ptr + 1) + 2;
ptr += *(ptr + 1) + 2;
}
len = opt_local->ip6po_rhinfo.ip6po_rhi_rthdr->ip6r_len;
if (len == 0)
new_len = 2;
else {
new_len = len + 2;
idx = (len / 2) - 1;
rthdr0 = (struct ip6_rthdr0 *)
opt_local->ip6po_rhinfo.ip6po_rhi_rthdr;
ptr = (caddr_t)rthdr0 + sizeof(struct ip6_rthdr0);
ip6rt_addr = (struct in6_addr *)ptr;
if (IN6_ARE_ADDR_EQUAL(&bcp->coa, ip6rt_addr + idx))
return 0;
}
rthdr0 = (struct ip6_rthdr0 *)
MALLOC(sizeof(struct ip6_rthdr0) +
(new_len / 2) * sizeof(struct in6_addr), M_TEMP, M_WAITOK);
if (rthdr0 == NULL)
return ENOBUFS;
bcopy((caddr_t)opt_local->ip6po_rhinfo.ip6po_rhi_rthdr,
(caddr_t)rthdr0, (len + 1) * 8);
bcopy((caddr_t)&bcp->coa, (caddr_t)rthdr0 + (len + 1) * 8,
sizeof(struct in6_addr));
rthdr0->ip6r0_len = new_len;
rthdr0->ip6r0_segleft = new_len / 2;
FREE(opt_local->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
opt_local->ip6po_rhinfo.ip6po_rhi_rthdr =
(struct ip6_rthdr *)rthdr0;
}
*opt = opt_local;
return 0;
}
void
mip6_align(dstopt, off)
struct ip6_dest *dstopt;
int *off;
{
int rest;
u_int8_t padlen;
u_int8_t padn;
padn = IP6OPT_PADN;
rest = *off % 8;
if (rest) {
padlen = 8 - rest;
if (rest == 7) {
bzero((caddr_t)dstopt + *off, 1);
*off += 1;
} else {
bzero((caddr_t)dstopt + *off, padlen);
bcopy(&padn, (caddr_t)dstopt + *off, 1);
padlen = padlen - 2;
bcopy(&padlen, (caddr_t)dstopt + *off + 1, 1);
*off += padlen + 2;
}
}
}
void
mip6_dest_offset(dstopt, off)
struct ip6_dest *dstopt;
int *off;
{
int ii;
u_int8_t opttype;
u_int8_t optlen;
u_int32_t len;
if (dstopt == NULL) {
*off = 0;
return;
}
len = (dstopt->ip6d_len + 1) << 3;
*off = 2;
for (ii = 2; ii < len;) {
bcopy((caddr_t)dstopt + ii, (caddr_t)&opttype, 1);
if (opttype == IP6OPT_PAD1) {
*off = ii;
ii += 1;
continue;
}
bcopy((caddr_t)dstopt + ii + 1, (caddr_t)&optlen, 1);
if (opttype == IP6OPT_PADN) {
*off = ii;
ii += 2 + optlen;
} else {
ii += 2 + optlen;
*off = ii;
}
}
}
int
mip6_add_ha(dstopt, off, src_addr, coa)
struct ip6_dest **dstopt;
int *off;
struct in6_addr *src_addr;
struct in6_addr *coa;
{
struct ip6_dest *new_opt;
struct ip6_dest *dest;
int ii;
int rest;
u_int8_t padn;
u_int8_t opttype;
u_int8_t optlen;
u_int8_t dstlen;
u_int32_t len;
dest = *dstopt;
if (dest == NULL) {
dest = (struct ip6_dest *)MALLOC(sizeof(struct ip6_dest) +
sizeof(struct mip6_opt_ha),
M_TEMP, M_WAITOK);
if (dest == NULL)
return ENOBUFS;
bzero(dest, sizeof(struct ip6_dest) + sizeof(struct mip6_opt_ha));
*off = 2;
} else {
len = (dest->ip6d_len + 1) << 3;
new_opt = (struct ip6_dest *)MALLOC(len +
sizeof(struct mip6_opt_ha),
M_TEMP, M_WAITOK);
if (new_opt == NULL)
return ENOBUFS;
bzero(new_opt, len + sizeof(struct mip6_opt_ha));
bcopy((caddr_t)dest, (caddr_t)new_opt, len);
FREE(dest, M_IP6OPT);
dest = new_opt;
}
padn = IP6OPT_PADN;
rest = *off % 4;
if (rest == 0) {
bzero((caddr_t)dest + *off, 2);
bcopy(&padn, (caddr_t)dest + *off, 1);
*off += 2;
} else if (rest == 1) {
bzero((caddr_t)dest + *off, 1);
*off += 1;
} else if (rest == 3) {
bzero((caddr_t)dest + *off, 3);
bcopy(&padn, (caddr_t)dest + *off, 1);
bcopy(&padn, (caddr_t)dest + *off + 1, 1);
*off += 3;
}
opttype = IP6OPT_HOME_ADDRESS;
optlen = IP6OPT_HALEN;
bcopy(&opttype, (caddr_t)dest + *off, 1);
*off += 1;
bcopy(&optlen, (caddr_t)dest + *off, 1);
*off += 1;
for (ii = 0; ii < 4; ii++) {
bcopy((caddr_t)&src_addr->s6_addr32[ii], (caddr_t)dest + *off, 4);
*off += 4;
}
mip6_align(dest, off);
dstlen = (*off >> 3) - 1;
bcopy(&dstlen, (caddr_t)dest + 1, 1);
src_addr->s6_addr32[0] = coa->s6_addr32[0];
src_addr->s6_addr32[1] = coa->s6_addr32[1];
src_addr->s6_addr32[2] = coa->s6_addr32[2];
src_addr->s6_addr32[3] = coa->s6_addr32[3];
*dstopt = dest;
return 0;
}
int
mip6_add_bu(dstopt, off, optbu, subopt)
struct ip6_dest **dstopt;
int *off;
struct mip6_opt_bu *optbu;
struct mip6_subbuf *subopt;
{
struct ip6_dest *new_opt;
struct ip6_dest *dest;
u_int8_t padn;
u_int8_t dstlen;
int offlen;
int rest;
int optlen;
int tmp16;
int tmp32;
int len;
int after, before;
if (optbu == NULL)
return 0;
dest = *dstopt;
if (dest == NULL) {
len = sizeof(struct ip6_dest) + sizeof(struct mip6_opt_bu) + 8;
if (subopt != NULL)
len += subopt->len;
dest = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
if (dest == NULL)
return ENOBUFS;
bzero(dest, len);
*off = 2;
} else {
len = (dest->ip6d_len + 1) << 3;
len += sizeof(struct mip6_opt_bu) + 8;
if (subopt != NULL)
len += subopt->len;
new_opt = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
if (new_opt == NULL)
return ENOBUFS;
bzero(new_opt, len);
bcopy((caddr_t)dest, (caddr_t)new_opt, (dest->ip6d_len + 1) << 3);
FREE(dest, M_IP6OPT);
dest = new_opt;
}
padn = IP6OPT_PADN;
rest = *off % 4;
if (rest == 0) {
bzero((caddr_t)dest + *off, 2);
bcopy(&padn, (caddr_t)dest + *off, 1);
*off += 2;
} else if (rest == 1) {
bzero((caddr_t)dest + *off, 1);
*off += 1;
} else if (rest == 3) {
bzero((caddr_t)dest + *off, 3);
bcopy(&padn, (caddr_t)dest + *off, 1);
bcopy(&padn, (caddr_t)dest + *off + 1, 1);
*off += 3;
}
offlen = *off + 1;
optbu->len = IP6OPT_BULEN;
bcopy((caddr_t)&optbu->type, (caddr_t)dest + *off, sizeof(optbu->type));
*off += sizeof(optbu->type);
bcopy((caddr_t)&optbu->len, (caddr_t)dest + *off, sizeof(optbu->len));
*off += sizeof(optbu->len);
bcopy((caddr_t)&optbu->flags, (caddr_t)dest + *off, sizeof(optbu->flags));
*off += sizeof(optbu->flags);
bcopy((caddr_t)&optbu->prefix_len, (caddr_t)dest + *off,
sizeof(optbu->prefix_len));
*off += sizeof(optbu->prefix_len);
tmp16 = htons(optbu->seqno);
bcopy((caddr_t)&tmp16, (caddr_t)dest + *off, sizeof(optbu->seqno));
*off += sizeof(optbu->seqno);
tmp32 = htonl(optbu->lifetime);
bcopy((caddr_t)&tmp32, (caddr_t)dest + *off, sizeof(optbu->lifetime));
*off += sizeof(optbu->lifetime);
optlen = optbu->len;
if (subopt) {
before = *off;
mip6_align(dest, off);
after = *off;
optlen += after - before;
bcopy((caddr_t)subopt->buffer, (caddr_t)dest + *off, subopt->len);
*off += subopt->len;
optlen += subopt->len;
optbu->len += subopt->len;
}
bcopy((caddr_t)&optlen, (caddr_t)dest + offlen, 1);
mip6_align(dest, off);
dstlen = (*off >> 3) - 1;
bcopy(&dstlen, (caddr_t)dest + 1, 1);
*dstopt = dest;
return 0;
}
int
mip6_add_ba(dstopt, off, optba, subopt)
struct ip6_dest **dstopt;
int *off;
struct mip6_opt_ba *optba;
struct mip6_subbuf *subopt;
{
struct ip6_dest *new_opt;
struct ip6_dest *dest;
u_int8_t padn;
u_int8_t dstlen;
int offlen;
int optlen;
int rest;
int tmp16;
int tmp32;
int len;
int after, before;
if (optba == NULL)
return 0;
dest = *dstopt;
if (dest == NULL) {
len = sizeof(struct ip6_dest) + sizeof(struct mip6_opt_ba) + 8;
if (subopt != NULL)
len += subopt->len;
dest = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
if (dest == NULL)
return ENOBUFS;
bzero(dest, len);
*off = 2;
} else {
len = (dest->ip6d_len + 1) << 3;
len += sizeof(struct mip6_opt_ba) + 8;
if (subopt != NULL)
len += subopt->len;
new_opt = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
if (new_opt == NULL)
return ENOBUFS;
bzero(new_opt, len);
bcopy((caddr_t)dest, (caddr_t)new_opt, (dest->ip6d_len + 1) << 3);
FREE(dest, M_IP6OPT);
dest = new_opt;
}
padn = IP6OPT_PADN;
rest = *off % 4;
if (rest == 1) {
bzero((caddr_t)dest + *off, 2);
bcopy(&padn, (caddr_t)dest + *off, 1);
*off += 2;
} else if (rest == 2) {
bzero((caddr_t)dest + *off, 1);
*off += 1;
} else if (rest == 0) {
bzero((caddr_t)dest + *off, 3);
bcopy(&padn, (caddr_t)dest + *off, 1);
bcopy(&padn, (caddr_t)dest + *off + 1, 1);
*off += 3;
}
offlen = *off + 1;
bcopy((caddr_t)&optba->type, (caddr_t)dest + *off, sizeof(optba->type));
*off += sizeof(optba->type);
bcopy((caddr_t)&optba->len, (caddr_t)dest + *off, sizeof(optba->len));
*off += sizeof(optba->len);
bcopy((caddr_t)&optba->status, (caddr_t)dest + *off,
sizeof(optba->status));
*off += sizeof(optba->status);
tmp16 = htons(optba->seqno);
bcopy((caddr_t)&tmp16, (caddr_t)dest + *off, sizeof(optba->seqno));
*off += sizeof(optba->seqno);
tmp32 = htonl(optba->lifetime);
bcopy((caddr_t)&tmp32, (caddr_t)dest + *off, sizeof(optba->lifetime));
*off += sizeof(optba->lifetime);
tmp32 = htonl(optba->refresh);
bcopy((caddr_t)&tmp32, (caddr_t)dest + *off, sizeof(optba->refresh));
*off += sizeof(optba->refresh);
optlen = IP6OPT_BALEN;
if (subopt) {
before = *off;
mip6_align(dest, off);
after = *off;
optlen += after - before;
bcopy((caddr_t)subopt->buffer, (caddr_t)dest + *off, subopt->len);
*off += subopt->len;
optlen += subopt->len;
optba->len += subopt->len;
}
bcopy((caddr_t)&optlen, (caddr_t)dest + offlen, 1);
mip6_align(dest, off);
dstlen = (*off >> 3) - 1;
bcopy(&dstlen, (caddr_t)dest + 1, 1);
*dstopt = dest;
return 0;
}
int
mip6_add_br(dstopt, off, optbr, subopt)
struct ip6_dest **dstopt;
int *off;
struct mip6_opt_br *optbr;
struct mip6_subbuf *subopt;
{
struct ip6_dest *new_opt;
struct ip6_dest *dest;
u_int8_t dstlen;
int offlen;
int rest;
int optlen;
int len;
int after, before;
if (optbr == NULL)
return 0;
dest = *dstopt;
if (dest == NULL) {
len = sizeof(struct ip6_dest) + sizeof(struct mip6_opt_br) + 8;
if (subopt != NULL)
len += subopt->len;
dest = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
if (dest == NULL)
return ENOBUFS;
bzero(dest, len);
*off = 2;
} else {
len = (dest->ip6d_len + 1) << 3;
len += sizeof(struct mip6_opt_br) + 8;
if (subopt != NULL)
len += subopt->len;
new_opt = (struct ip6_dest *)MALLOC(len, M_TEMP, M_WAITOK);
if (new_opt == NULL)
return ENOBUFS;
bzero(new_opt, len);
bcopy((caddr_t)dest, (caddr_t)new_opt, (dest->ip6d_len + 1) << 3);
FREE(dest, M_IP6OPT);
dest = new_opt;
}
rest = *off % 4;
if ((rest == 1) || (rest == 3)) {
bzero((caddr_t)dest + *off, 1);
*off += 1;
}
offlen = *off +1;
bcopy((caddr_t)&optbr->type, (caddr_t)dest + *off, sizeof(optbr->type));
*off += sizeof(optbr->type);
bcopy((caddr_t)&optbr->len, (caddr_t)dest + *off, sizeof(optbr->len));
*off += sizeof(optbr->len);
optlen = IP6OPT_BRLEN;
if (subopt) {
before = *off;
mip6_align(dest, off);
after = *off;
optlen += after - before;
bcopy((caddr_t)subopt->buffer, (caddr_t)dest + *off, subopt->len);
*off += subopt->len;
optlen += subopt->len;
optbr->len += subopt->len;
}
bcopy((caddr_t)&optlen, (caddr_t)dest + offlen, 1);
mip6_align(dest, off);
dstlen = (*off >> 3) - 1;
bcopy(&dstlen, (caddr_t)dest + 1, 1);
*dstopt = dest;
return 0;
}
int
mip6_store_subopt(subbuf, subopt)
struct mip6_subbuf **subbuf;
caddr_t subopt;
{
struct mip6_subopt_id *uid;
struct mip6_subopt_hal *hal;
struct mip6_subopt_coa *altcoa;
struct mip6_subbuf *buf;
u_int8_t pad1, padn;
u_int16_t tmp16;
int rest, no, ii, padlen;
if (subopt == NULL)
return 0;
buf = *subbuf;
if (buf == NULL) {
buf = (struct mip6_subbuf *)MALLOC(sizeof(struct mip6_subbuf),
M_TEMP, M_WAITOK);
if (buf == NULL)
return 1;
bzero(buf, sizeof(struct mip6_subbuf));
}
padn = IP6OPT_PADN;
pad1 = IP6OPT_PAD1;
switch (*subopt) {
case IP6SUBOPT_UNIQUEID:
uid = (struct mip6_subopt_id *)subopt;
if (uid->len != IP6OPT_UIDLEN)
return 1;
rest = buf->len % 2;
if (rest == 1) {
bcopy(&pad1, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
}
bcopy(&uid->type, (caddr_t)buf->buffer + buf->len,
sizeof(uid->type));
buf->len += sizeof(uid->type);
bcopy(&uid->len, (caddr_t)buf->buffer + buf->len,
sizeof(uid->len));
buf->len += sizeof(uid->len);
tmp16 = htons(uid->id);
bcopy(&tmp16, (caddr_t)buf->buffer + buf->len, sizeof(tmp16));
buf->len += sizeof(tmp16);
break;
case IP6SUBOPT_HALIST:
hal = (struct mip6_subopt_hal *)subopt;
if (hal->len % IP6OPT_HALISTLEN)
return 1;
rest = buf->len % 8;
if (rest > 3) {
padlen = rest - 4;
bcopy(&padn, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
bcopy(&padlen, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
bzero((caddr_t)buf->buffer + buf->len, padlen);
buf->len += padlen;
} else if (rest == 3) {
bcopy(&pad1, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
} else if (rest <= 1) {
padlen = rest + 4;
bcopy(&padn, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
bcopy(&padlen, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
bzero((caddr_t)buf->buffer + buf->len, padlen);
buf->len += padlen;
}
bcopy(&hal->type, (caddr_t)buf->buffer + buf->len,
sizeof(hal->type));
buf->len += sizeof(hal->type);
bcopy(&hal->len, (caddr_t)buf->buffer + buf->len,
sizeof(hal->len));
buf->len += sizeof(hal->len);
no = hal->len / IP6OPT_HALISTLEN;
for (ii = 0; ii < no; ii++) {
bcopy(&hal->halist[ii], (caddr_t)buf->buffer + buf->len,
sizeof(hal->halist));
buf->len += sizeof(hal->halist);
}
break;
case IP6SUBOPT_ALTCOA:
altcoa = (struct mip6_subopt_coa *)subopt;
if (altcoa->len % IP6OPT_COALEN)
return 1;
rest = buf->len % 8;
if (rest > 3) {
padlen = rest - 4;
bcopy(&padn, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
bcopy(&padlen, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
bzero((caddr_t)buf->buffer + buf->len, padlen);
buf->len += padlen;
} else if (rest == 3) {
bcopy(&pad1, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
} else if (rest <= 1) {
padlen = rest + 4;
bcopy(&padn, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
bcopy(&padlen, (caddr_t)buf->buffer + buf->len, 1);
buf->len += 1;
bzero((caddr_t)buf->buffer + buf->len, padlen);
buf->len += padlen;
}
bcopy(&altcoa->type, (caddr_t)buf->buffer + buf->len,
sizeof(altcoa->type));
buf->len += sizeof(altcoa->type);
bcopy(&altcoa->len, (caddr_t)buf->buffer + buf->len,
sizeof(altcoa->len));
buf->len += sizeof(altcoa->len);
bcopy(&altcoa->coa, (caddr_t)buf->buffer + buf->len,
sizeof(altcoa->coa));
buf->len += sizeof(altcoa->coa);
break;
default:
}
*subbuf = buf;
return 0;
}