/* * Copyright (c) 1995, 1996, 1998 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. 3. All advertising * materials mentioning features or use of this software must display the * following acknowledgement: This product includes software developed by the * University of California, Berkeley and its contributors. 4. Neither the * name of the University nor the names of its contributors may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Berkeley mobility protocol to enable "horizontal" mobility across cells of * a wireless network, as well as "vertical" handoffs across cells of * different overlay wireless networks. Authors: Srinivasan Seshan, Mark * Stemm, Hari Balakrishnan, U.C. Berkeley. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BASESTATION #include #endif extern struct protosw inetsw[]; extern u_char ip_protox[IPPROTO_MAX]; struct mbuf *ip_encap_pkt(); #ifdef MOBILE_HOST struct mobileip_ids mip_ids; struct mobilemh_state mip_state; int printNextNonBlocked = 0; #endif #ifdef BASESTATION struct mobileip_state mip_state; #endif /* * Initialize state for the mip protocol. This function is called at boot * time. */ void mip_init() { int i, j; printf("MIP initialization... "); #ifdef BASESTATION /* for BS */ mip_state.num_mobile = 0; /* for the moment */ for (i = 0; i < MAX_MOBILE_HOSTS; i++) { mip_state.trans[i].state = MIP_UNUSED; mip_state.trans[i].lst_pkt = MIP_MAX_LOST - 1; mip_state.trans[i].hdrOnly = 0; for (j = 0; j < MIP_MAX_LOST; j++) { mip_state.trans[i].pkts[j] = (mip_packet_t *) mip_malloc(sizeof(mip_packet_t)); mip_state.trans[i].pkts[j]->mb = 0; } } #endif #ifdef MOBILE_HOST /* for MH */ for (i = 0; i < MIP_NUM_IDS; i++) mip_ids.ip_id[i] = 0; mip_ids.lst_pkt = 0; mip_state.mip_mh_addr.s_addr = 0; mip_state.mipBlockedIfs[0][0] = NULL; mip_state.mipHandoffCtlrPid = -1; mip_state.mipBurstThreshold = INT_MAX; mip_state.tunnel_addr.s_addr = INADDR_ANY; for (j = 0; j < MIP_MAX_LOST; j++) { mip_state.pkts[j] = (mip_packet_t *) mip_malloc(sizeof(mip_packet_t)); mip_state.pkts[j]->mb = 0; } mip_state.lst_pkt = MIP_MAX_LOST - 1; for (j=0; jiph.len = ip->ip_len; packet->iph.ttl = ip->ip_ttl; packet->iph.id = ip->ip_id; packet->iph.off = ip->ip_off; if (copy) packet->mb = m_copym(m, 0, M_COPYALL, M_DONTWAIT); else packet->mb = m; } /* * malloc() function for the Berkeley MIP protocol. */ void * mip_malloc(int size) { void *m; if ((m = malloc(size, M_MOBIP, M_NOWAIT)) == (void *) 0) { printf("mip_malloc: out of memory\n"); panic("mip_malloc"); } return m; } #endif /* BASESTATION */ #ifdef MOBILE_HOST /* * Like mip_find_entry, but works on dont touch table instead. * If entry that matches addr is found, returns pointer to * entry. Otherwise, returns pointer to unused * (and unitialized) slot. */ mobilemh_dt * mip_find_dt_entry(struct in_addr addr) { int i; for (i = 0; i inm_addr.s_addr == addr.s_addr) { if (mip_state.trans[i].state == MIP_UNENCAP_BUF || mip_state.trans[i].state == MIP_UNENCAP_FWD) return (&mip_state.trans[i]); } } return (NULL); } else { for (i = 0; i < mip_state.num_mobile; i++) { if (mip_state.trans[i].ins_addr.s_addr == addr.s_addr) { if (mip_state.trans[i].state == MIP_ENCAP || mip_state.trans[i].state == MIP_UNENCAP_FWD) return (&mip_state.trans[i]); } } return (NULL); } } struct mobileip_trans * mip_find_chsrc_entry(struct in_addr addr) { int i; if (IN_MULTICAST(ntohl(addr.s_addr))) { for (i = 0; i < mip_state.num_mobile; i++) { if (mip_state.trans[i].multi_addr->inm_addr.s_addr == addr.s_addr) { if (mip_state.trans[i].state == MIP_CHSRC) return (&mip_state.trans[i]); } } return (NULL); } else { for (i = 0; i < mip_state.num_mobile; i++) { if (mip_state.trans[i].ins_addr.s_addr==addr.s_addr) { if (mip_state.trans[i].state == MIP_CHSRC) return (&mip_state.trans[i]); } } return (NULL); } } #endif /* BASESTATION */ #ifdef MOBILE_HOST /* * Takes an input packet and consults list of "don't touch" * addresses. Any packets destined for hosts on this list * are simply passed on. Any packets for hosts not on this * list have their source address changed or are tunneled * according to the "insert source" and "tunnel" addresses. * This function returns 1 if packet has been handled, 0 * to continue packet processing. */ int mip_output_ctrl(struct mbuf *m, struct in_ifaddr *ia) { register struct ip *ip = mtod(m, struct ip *); register int hlen = sizeof (struct ip); int i; /* * Search through the list of "don't touch" addresses. * If ip->dst is on the list, then just pass the packet on. */ for(i=0; iip_dst.s_addr){ return 0; } } /* If packet is ip proto 2 (encapsulated), don't handle it */ if (ip->ip_p == IPPROTO_IPIP){ return 0; } /* * Insert and tunnel packets, if necessary. */ if (mip_state.mip_mh_addr.s_addr != INADDR_ANY){ ip->ip_src.s_addr = mip_state.mip_mh_addr.s_addr; } /* * Add a new IP header with the interface address as the * source and the tunnel address as the dest. */ if (mip_state.tunnel_addr.s_addr != INADDR_ANY){ #ifdef DEBUG printf("Encaping packet with s=%x, d=%x\n", IA_SIN(ia)->sin_addr.s_addr, mip_state.tunnel_addr.s_addr); #endif m=ip_encap_pkt(m, IA_SIN(ia)->sin_addr, mip_state.tunnel_addr, hlen); ip_output(m, NULL, NULL, 0, NULL); return 1; } else { return 0; } } /* * Takes a packet, and throws it away if it came from a blocked interface and * was destined for this mobile (i.e. not a broadcast packet). Also updates * count of number of consecutive packets from a single interface and sends a * signal to the Handoff Controller if it is more than the specified * threshold. */ int mip_ctrl(struct mbuf * m) { register struct proc *p; /* * Update our count of consecutive packets and send a signal if * necessary */ if (strcmp(m->m_pkthdr.rcvif->if_name, mip_state.mipBurstIf) == 0) { mip_state.mipBurstCount++; if (mip_state.mipBurstCount == mip_state.mipBurstThreshold) { /* * printf("%d packets from %s (more than %d)\n", * mip_state.mipBurstCount, mip_state.mipBurstIf, * mip_state.mipBurstThreshold); */ if (mip_state.mipHandoffCtlrPid != -1) { p = pfind(mip_state.mipHandoffCtlrPid); if (p == NULL) { printf("No Process registered--no signal sent\n"); } else { psignal(p, SIGUSR1); } } } } else { strcpy(mip_state.mipBurstIf, m->m_pkthdr.rcvif->if_name); mip_state.mipBurstCount = 0; } if (strcmp(m->m_pkthdr.rcvif->if_name, mip_state.mipBlockedIfs[0]) != 0 && printNextNonBlocked == 1) { struct timeval start; printNextNonBlocked = 0; microtime(&start); printf("Next packet after blocked at %d secs, %d usecs\n", start.tv_sec, start.tv_usec); } /* Throw away this packet if the interface was blocked */ /* and if the source IP address isn't a broadcast address */ if (strcmp(m->m_pkthdr.rcvif->if_name, mip_state.mipBlockedIfs[0]) == 0 && (m->m_flags & M_BCAST) == 0) { /* * printf("Discarding packet from %s\n", * m->m_pkthdr.rcvif->if_name); */ int nxt_pk = (mip_state.lst_pkt + 1) % MIP_MAX_LOST; mip_packet_t *pkt = mip_state.pkts[nxt_pk]; m->m_flags &= ~M_MCAST; mip_state.lst_pkt = nxt_pk; if (pkt->mb) { m_freem(pkt->mb); pkt->mb = 0; } save_mippkt(pkt, m, 0); return (1); } return (0); } #endif #ifdef BASESTATION int mip_ctrl(struct mbuf * m) { /* * Return 1 to process next packet; return 0 to continue packet * processing. */ struct tcpiphdr *tcpip_hdr = mtod(m, struct tcpiphdr *); register struct ip *ip = mtod(m, struct ip *); struct mobileip_trans *trans; if (ip->ip_p == IPPROTO_IGMP) return (0); trans = mip_find_entry(ip->ip_dst); if (trans == NULL) { trans = mip_find_chsrc_entry(ip->ip_src); if (trans == NULL) return (0); } switch (trans->state) { case MIP_UNENCAP_BUF: { int nxt_pk = (trans->lst_pkt + 1) % MIP_MAX_LOST; mip_packet_t *pkt = trans->pkts[nxt_pk]; /* * If the mobile has requested to foward headers, * make a copy of the header and send it on. */ if (trans->hdrOnly) { struct mbuf *n; struct ip *ip2; struct ip temp; if (ip->ip_p == IPPROTO_IPIP) { m_adj(m, ip->ip_hl << 2); ip = mtod(m, struct ip *); } n = m_copym(m, 0, ip->ip_hl << 2, M_DONTWAIT); if (n == NULL) { printf("Couldn't copy in mip_ctrl\n"); return (1); } ip2 = mtod(n, struct ip *); /* * Save a copy of m's header structures -- * forwarding n will make m's header go away. */ bcopy((char *) ip, (char *) &temp, sizeof(struct ip)); /* Change the size of the packet. */ ip2->ip_len = ip2->ip_hl << 2; n->m_flags &= ~M_MCAST; /* Forward the header on. */ ip_forward(n, 1); /* Restore the headers back to m. */ bcopy((char *) &temp, (char *) ip, sizeof(struct ip)); } #ifdef DEBUG printf("unencap buf\n"); #endif /* * Should just check for ip protocol number 4 = encap * and do this for any packet. For now do it only for * mobiles */ if (ip->ip_p == IPPROTO_IPIP) m_adj(m, ip->ip_hl << 2); m->m_flags &= ~M_MCAST; trans->lst_pkt = nxt_pk; if (pkt->mb) { m_freem(pkt->mb); pkt->mb = 0; } save_mippkt(pkt, m, 0); snoop_ctrl(m, SNOOP_BUF, FROM_WIRED, 1); return (1); } case MIP_UNENCAP_FWD: { /* found */ #ifdef DEBUG printf("unencap fwd\n"); #endif /* * Should just check for ip protocol number 4 = encap * and do this for any packet. For now do it only for * mobiles. */ if (ip->ip_p == IPPROTO_IPIP) { m_adj(m, ip->ip_hl << 2); ip = mtod(m, struct ip *); if (mip_find_entry(ip->ip_dst) == NULL) { #ifdef DEBUG printf("mip_ctrl: discarding packet for unregistered host\n"); #endif m_freem(m); return (1); } } m->m_flags &= ~M_MCAST; snoop_ctrl(m, SNOOP_FWD, FROM_WIRED, 1); return (1); } case MIP_ENCAP: { register struct ip_moptions *imo; struct ip_moptions simo; register int error; struct ip *newip; #ifdef DEBUG printf("encap\n"); #endif /* * IP-in-IP encapsulation: prepend a new IP header * with multicast destination and encapsulating * agent as source. */ M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); if (m == 0) { error = ENOBUFS; return (0); } newip = mtod(m, struct ip *); newip->ip_len = ip->ip_len + sizeof(struct ip); newip->ip_off = ip->ip_off; newip->ip_ttl = trans->multicast_ttl; newip->ip_off = IP_DF; newip->ip_p = IPPROTO_IPIP; newip->ip_tos = ip->ip_tos; newip->ip_src.s_addr = INADDR_ANY; newip->ip_dst.s_addr = trans->multi_addr->inm_addr.s_addr; m->m_flags |= M_MCAST; imo = &simo; imo->imo_multicast_ifp = trans->ifp; imo->imo_multicast_ttl = trans->multicast_ttl; imo->imo_multicast_loop = 0; error = ip_output(m, NULL, NULL, 0, imo); return (1); } case MIP_CHSRC: { #ifdef DEBUG printf("chsrc\n"); #endif ip->ip_src.s_addr = trans->ins_addr_new.s_addr; return (0); } default: #ifdef DEBUG printf("addr %d, state %d\n", ip->ip_dst.s_addr, trans->state); #endif return (0); } /* end switch trans->state */ return (0); } #endif /* BASESTATION */ int ip_setmipoptions(optname, req) int optname; struct ip_mobreq *req; { register int error = 0; u_char loop; struct ip_mreq mreq; register struct ifnet *ifp; struct route ro; register struct sockaddr_in *dst; struct mobileip_trans *trans; mobilemh_dt *entry; struct timeval start; int i; switch (optname) { #ifdef MOBILE_HOST case IP_ADD_DT_ADDR: if (mip_state.num_dt >= MAX_DT_ENTRIES){ error = ETOOMANYREFS; break; } entry = mip_find_dt_entry(req->imr_ins_addr); if (entry!=NULL){ error = EEXIST; break; } entry = &(mip_state.dt[mip_state.num_dt]); entry->addr.s_addr = req->imr_ins_addr.s_addr; entry->state = MIP_DT_USED; mip_state.num_dt++; break; case IP_CLR_DT_ADDR: for(i=0; iimr_ins_addr.s_addr; break; case IP_CLR_MH_ADDR: mip_state.mip_mh_addr.s_addr = 0; break; case IP_IF_ON: /* * Allow packets from this interface to be passed up to * higher layers */ if (strcmp(mip_state.mipBlockedIfs[0], req->blockedIf) == 0) { mip_state.mipBlockedIfs[0][0] = NULL; microtime(&start); printf("If %s turned back on at %d secs, %d usecs\n", req->blockedIf, start.tv_sec, start.tv_usec); /* * Also, if this interface has received a burst, * forward the last few packets that were buffered by * this interface to higher layers. */ if (mip_state.mipBurstCount >= mip_state.mipBurstThreshold) { int numForward, j; /* * Also forward the packets that we have had * buffered. The number of packets to forward * is the min of the burst length and the * number of packets in the circular buffer */ numForward = min(mip_state.mipBurstCount, MIP_MAX_LOST); j = (mip_state.lst_pkt - numForward) % MIP_MAX_LOST; if (j < 0) { j += MIP_MAX_LOST; } printf("Sending from %d to %d\n", j, mip_state.lst_pkt); do { if (mip_state.pkts[j]->mb) { struct ip *ip; ip = mtod(mip_state.pkts[j]->mb, struct ip *); ip->ip_len = mip_state.pkts[j]->iph.len; ip->ip_id = mip_state.pkts[j]->iph.id; ip->ip_ttl = mip_state.pkts[j]->iph.ttl; ip->ip_off = mip_state.pkts[j]->iph.off; ip->ip_tos |= IPTOS_LOWDELAY; if (ip->ip_p == IPPROTO_TCP) { m_freem(mip_state.pkts[j]->mb); mip_state.pkts[j]->mb = 0; } else { register struct ip *ip; int hlen; microtime(&start); printf("forwarding ip id %d, j=%d at %d %d\n", ip->ip_id, j, start.tv_sec, start.tv_usec); ip = mtod(mip_state.pkts[j]->mb, struct ip *); hlen = ip->ip_hl << 2; ipstat.ips_delivered++; (*inetsw[ip_protox[ip->ip_p]].pr_input) (mip_state.pkts[j]->mb, hlen); mip_state.pkts[j]->mb = 0; } } mip_state.pkts[j]->iph.id = 0; j = (j + 1) % MIP_MAX_LOST; } while (j != (mip_state.lst_pkt + 1) % MIP_MAX_LOST); } } break; case IP_IF_OFF: /* * Don't allow packets from this interface to be passed up to * higher layers */ microtime(&start); strcpy(mip_state.mipBlockedIfs[0], req->blockedIf); printf("If %s blocked at %d secs, %d usecs\n", req->blockedIf, start.tv_sec, start.tv_usec); /* * Also, print out when the next packet arrives an interface * other than the blocked interface--this is for timing * purposes */ printNextNonBlocked = 1; break; case IP_NOTIFY_PID: /* * This is the process id of the process that should be sent * a signal when many packets in a row come in via a single * interface */ mip_state.mipHandoffCtlrPid = req->handoffCtlrPid; printf("HC pid=%d\n", req->handoffCtlrPid); break; case IP_NOTIFY_THRESHOLD: /* * This is the number of consecutive packets that should be * sent via a single interface before notifying the handoff * controller. */ mip_state.mipBurstThreshold = req->burstThreshold; printf("burst threshold=%d\n", req->burstThreshold); break; case IP_TUNNEL_ADDR: mip_state.tunnel_addr = req->imr_ins_addr; break; #endif #ifdef BASESTATION case IP_ADD_ENCAP: #ifdef DEBUG printf("ip add encap\n"); #endif if (mip_state.num_mobile >= MAX_MOBILE_HOSTS) { error = ETOOMANYREFS; break; } trans = mip_find_entry(req->imr_ins_addr); if (trans != NULL) { error = EEXIST; break; } trans = &(mip_state.trans[mip_state.num_mobile]); trans->ins_addr.s_addr = req->imr_ins_addr.s_addr; /* * Set the IP time-to-live for outgoing multicast packets. */ trans->multicast_ttl = req->imr_multicast_ttl; if (!IN_MULTICAST(ntohl(req->imr_multi_addr.inm_addr.s_addr))) { error = EINVAL; break; } mreq.imr_interface.s_addr = req->imr_ifaddr.s_addr; mreq.imr_multiaddr = req->imr_multi_addr.inm_addr; /* * INADDR_ANY is used to remove a previous selection. When no * interface is selected, a default one is chosen every time * a multicast packet is sent. */ if (mreq.imr_interface.s_addr == INADDR_ANY) { ro.ro_rt = NULL; dst = (struct sockaddr_in *) & ro.ro_dst; dst->sin_len = sizeof(*dst); dst->sin_family = AF_INET; dst->sin_addr = mreq.imr_multiaddr; rtalloc(&ro); if (ro.ro_rt == NULL) { error = EADDRNOTAVAIL; break; } ifp = ro.ro_rt->rt_ifp; rtfree(ro.ro_rt); } else { /* * The selected interface is identified by its local * IP address. Find the interface and confirm that * it supports multicasting. */ INADDR_TO_IFP(req->imr_ifaddr, ifp); } if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { error = EADDRNOTAVAIL; break; } if (mreq.imr_interface.s_addr == INADDR_ANY) trans->ifp = NULL; else trans->ifp = ifp; /* * Everything looks good; add a new record to the multicast * address list for the given interface. */ if ((trans->multi_addr = in_addmulti(&mreq.imr_multiaddr, ifp)) == NULL) { error = ENOBUFS; break; } trans->state = MIP_ENCAP; mip_state.num_mobile++; break; case IP_ADD_CHSRC: #ifdef DEBUG printf("ip add chsrc\n"); #endif if (mip_state.num_mobile >= MAX_MOBILE_HOSTS) { error = ETOOMANYREFS; break; } trans = mip_find_chsrc_entry(req->imr_ins_addr); if (trans != NULL) { error = EEXIST; break; } trans = &(mip_state.trans[mip_state.num_mobile]); trans->ins_addr.s_addr = req->imr_ins_addr.s_addr; trans->ins_addr_new.s_addr = req->imr_ins_addr_new.s_addr; trans->state = MIP_CHSRC; mip_state.num_mobile++; break; case IP_ADD_UNENCAP: #ifdef DEBUG printf("ip add unencap\n"); #endif if (mip_state.num_mobile >= MAX_MOBILE_HOSTS) { error = ETOOMANYREFS; break; } trans = mip_find_entry(req->imr_multi_addr.inm_addr); if (trans != NULL) { error = EEXIST; break; } trans = &(mip_state.trans[mip_state.num_mobile]); trans->ins_addr.s_addr = req->imr_ins_addr.s_addr; /* * Set the IP time-to-live for outgoing multicast packets. */ trans->multicast_ttl = req->imr_multicast_ttl; if (!IN_MULTICAST(ntohl(req->imr_multi_addr.inm_addr.s_addr))) { error = EINVAL; break; } mreq.imr_interface = req->imr_ifaddr; mreq.imr_multiaddr = req->imr_multi_addr.inm_addr; /* * INADDR_ANY is used to remove a previous selection. When no * interface is selected, a default one is chosen every time * a multicast packet is sent. if (mreq.imr_interface.s_addr * == INADDR_ANY) { */ ro.ro_rt = NULL; dst = (struct sockaddr_in *) & ro.ro_dst; dst->sin_len = sizeof(*dst); dst->sin_family = AF_INET; dst->sin_addr = mreq.imr_multiaddr; rtalloc(&ro); if (ro.ro_rt == NULL) { error = EADDRNOTAVAIL; break; } ifp = ro.ro_rt->rt_ifp; rtfree(ro.ro_rt); /* * } else { The selected interface is identified by its local * IP address. Find the interface and confirm that it * supports multicasting. INADDR_TO_IFP(req->imr_ifaddr, * ifp); } */ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { error = EADDRNOTAVAIL; break; } trans->ifp = ifp; /* * Everything looks good; add a new record to the multicast * address list for the given interface. */ if ((trans->multi_addr = in_addmulti(&mreq.imr_multiaddr, ifp)) == NULL) { error = ENOBUFS; break; } mip_state.num_mobile++; trans->state = MIP_UNENCAP_BUF; break; case IP_DEL_ENCAP: #ifdef DEBUG printf("ip del encap\n"); #endif trans = mip_find_entry(req->imr_ins_addr); if (trans == NULL || trans->state != MIP_ENCAP) { error = EADDRNOTAVAIL; break; } /* * Give up the multicast address record to which the * membership points. */ in_delmulti(trans->multi_addr, NULL); /* * Remove the gap in the membership array. */ trans->state = MIP_UNUSED; mip_del_entry(trans); break; case IP_DEL_CHSRC: #ifdef DEBUG printf("ip del chsrc\n"); #endif trans = mip_find_entry(req->imr_ins_addr); if (trans == NULL || trans->state != MIP_CHSRC) { error = EADDRNOTAVAIL; break; } /* * Remove the gap in the membership array. */ trans->state = MIP_UNUSED; mip_del_entry(trans); break; case IP_DEL_UNENCAP: { int j; #ifdef DEBUG printf("ip del unencap\n"); #endif /* * Drop a multicast group membership. Group must be a * valid IP multicast address. */ if (!IN_MULTICAST(ntohl(req->imr_multi_addr.inm_addr.s_addr))) { error = EINVAL; break; } trans = mip_find_entry(req->imr_multi_addr.inm_addr); if (trans == NULL || !(trans->state == MIP_UNENCAP_BUF || trans->state == MIP_UNENCAP_FWD)) { error = EADDRNOTAVAIL; break; } snoop_mip_change(trans->ins_addr, trans->state, MIP_UNUSED); trans->state = MIP_UNUSED; trans->lst_pkt = MIP_MAX_LOST - 1; for (j = 0; j < MIP_MAX_LOST; j++) { trans->pkts[j]->iph.id = 0; if (trans->pkts[j]->mb) { m_freem(trans->pkts[j]->mb); trans->pkts[j]->mb = 0; } } /* * Give up the multicast address record to which the * membership points. */ in_delmulti(trans->multi_addr, NULL); /* * Remove the gap in the membership array. */ mip_del_entry(trans); } break; case IP_DIS_UNENCAP: { #ifdef DEBUG printf("ip dis unencap\n"); #endif /* * Drop a multicast group membership. Group must be a * valid IP multicast address. */ if (!IN_MULTICAST(ntohl(req->imr_multi_addr.inm_addr.s_addr))) { error = EINVAL; break; } trans = mip_find_entry(req->imr_multi_addr.inm_addr); if (trans == NULL || (trans->state != MIP_UNENCAP_FWD && trans->hdrOnly == req->hdrOnly)) { error = EADDRNOTAVAIL; break; } snoop_mip_change(trans->ins_addr, trans->state, MIP_UNENCAP_BUF); trans->state = MIP_UNENCAP_BUF; trans->hdrOnly = req->hdrOnly; break; } case IP_ENA_UNENCAP:{ int j, found = 0, empty = 1, from, to; int idx = req->ids.lst_pkt; u_short id = req->ids.ip_id[idx]; #ifdef DEBUG printf("ip ena unencap\n"); #endif /* * Drop a multicast group membership. Group must be a * valid IP multicast address. */ if (!IN_MULTICAST(ntohl(req->imr_multi_addr.inm_addr.s_addr))) { error = EINVAL; break; } trans = mip_find_entry(req->imr_multi_addr.inm_addr); if (trans == NULL || trans->state != MIP_UNENCAP_BUF) { error = EADDRNOTAVAIL; break; } snoop_mip_change(trans->ins_addr, trans->state, MIP_UNENCAP_FWD); trans->state = MIP_UNENCAP_FWD; trans->hdrOnly = 0; /* resync buffers */ if (idx < 0 || idx >= MIP_NUM_IDS) { req->ids.lst_pkt = idx = 0; found = 1; to = from; } else { from = to = (trans->lst_pkt + 1) % MIP_MAX_LOST; do { #ifdef DEBUG printf("resyncing buffers to %d\n", id); #endif j = trans->lst_pkt; do { if (trans->pkts[j]->iph.id == id) { found = 1; empty = 0; to = (j + 1) % MIP_MAX_LOST; break; } else if (trans->pkts[j]->mb != 0) empty = 0; (j == 0) ? (j = MIP_MAX_LOST - 1) : j--; } while (j != (trans->lst_pkt + 1) % MIP_MAX_LOST); idx = (idx + 1) % MIP_NUM_IDS; id = req->ids.ip_id[idx]; } while (!found && idx != req->ids.lst_pkt); if (empty) { trans->lst_pkt = MIP_MAX_LOST - 1; break; } } if (found) { /* added to clear all if found and send none */ /* to = from; */ j = from; do { if (trans->pkts[j]->mb) { m_freem(trans->pkts[j]->mb); trans->pkts[j]->mb = 0; } trans->pkts[j]->iph.id = 0; j = (j + 1) % MIP_MAX_LOST; } while (j != to); } else { /* added to send full buffer if not found */ /* * to = (from + MIP_MAX_LOST/2) % * MIP_MAX_LOST; j = from; do { if * (trans->pkts[j]->mb) { * m_freem(trans->pkts[j]->mb); * trans->pkts[j]->mb = 0; } * trans->pkts[j]->iph.id = 0; j = (j +1) % * MIP_MAX_LOST; } while (j != to); */ } #ifdef DEBUG printf("clearing buffers from %d to %d\n", from, to); #endif #ifdef DEBUG printf("sending buffers from %d to %d\n", to, trans->lst_pkt); #endif j = to; do { if (trans->pkts[j]->mb) { struct ip *ip; ip = mtod(trans->pkts[j]->mb, struct ip *); ip->ip_len = trans->pkts[j]->iph.len; ip->ip_id = trans->pkts[j]->iph.id; ip->ip_ttl = trans->pkts[j]->iph.ttl; ip->ip_off = trans->pkts[j]->iph.off; ip->ip_tos |= IPTOS_LOWDELAY; if (ip->ip_p == IPPROTO_TCP) { m_freem(trans->pkts[j]->mb); trans->pkts[j]->mb = 0; } else { #ifdef DEBUG printf("forwarding ip id %d\n", ip->ip_id); #endif ip_forward(trans->pkts[j]->mb, 1); trans->pkts[j]->mb = 0; } } else { #ifdef DEBUG printf("null mbuf in trans, j=%d\n", j); #endif } trans->pkts[j]->iph.id = 0; j = (j + 1) % MIP_MAX_LOST; } while (j != (trans->lst_pkt + 1) % MIP_MAX_LOST); trans->lst_pkt = MIP_MAX_LOST - 1; } break; case IP_CLR_ALL:{ char *foo; int j, k; #ifdef DEBUG printf("ip clr all\n"); #endif /* * Find the membership in the membership array. */ for (j = 0; j < mip_state.num_mobile; j++) { trans = &(mip_state.trans[j]); if (trans->state != MIP_CHSRC && trans->multi_addr != NULL) in_delmulti(trans->multi_addr, NULL); snoop_mip_change(trans->ins_addr, trans->state, MIP_UNUSED); trans->state = MIP_UNUSED; for (k = 0; k < MIP_MAX_LOST; k++) { if (trans->pkts[k]->mb) { m_freem(trans->pkts[k]->mb); trans->pkts[k]->mb = 0; } } } foo = (char *) &mip_state; for (j = 0; j < sizeof(mip_state); j++) foo[j] = 0; mip_init(); } break; #endif /* BASESTATION */ default: error = EOPNOTSUPP; break; } return (error); } int ip_getmipoptions(optname, mp) int optname; register struct mbuf **mp; { struct mobileip_ids *ids; struct mobilemh_state *state; *mp = m_get(M_WAIT, MT_SOOPTS); switch (optname) { #ifdef MOBILE_HOST case IP_GET_IDS: #if 0 printf("get ids: %d: %d %d %d\n", mip_ids.lst_pkt, mip_ids.ip_id[0], mip_ids.ip_id[1], mip_ids.ip_id[2]); #endif ids = mtod(*mp, struct mobileip_ids *); (*mp)->m_len = sizeof(struct mobileip_ids); bcopy((char *) &mip_ids, ids, sizeof(struct mobileip_ids)); return (0); break; case IP_GET_MHSTATE: state = mtod(*mp, struct mobilemh_state *); (*mp)->m_len = sizeof(struct mobilemh_state); bcopy((char *) &mip_state, state, sizeof(struct mobilemh_state)); return (0); break; #endif /* MOBILE_HOST */ default: return (EOPNOTSUPP); } }