/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipppp_pppmp.c,v $ $Revision: 1.6.10.9 $ * $Source: /home/interpeak/CVSRoot/ipppp/src/ipppp_pppmp.c,v $ * $Author: qsun $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Timothy Neale ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * * Interpeak PPP implementation of multilink for IPLITE and IPNET. * */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include "ipppp_config.h" #include "ipppp_h.h" #ifdef IPPPP_USE_PPPMP #ifdef IPCOM_USE_AUTH_RADIUS #include #include #endif #ifdef IPDHCPS6 #include #endif #ifdef IPNET #include #elif defined(IPLITE) #include #else #error MUST_CHOOSE_STACK #endif /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #ifdef IPPPP_PPPMP_FRAGMENT_TIMER #ifdef IPNET #define IPSTACK_TIMEOUT_SCHEDULE ipnet_timeout_schedule #define IPSTACK_TIMEOUT_CANCEL ipnet_timeout_cancel #define Ipstack_timeout_handler Ipnet_timeout_handler #define Ipstack_timeout Ipnet_timeout #else #define IPSTACK_TIMEOUT_SCHEDULE iplite_timeout_schedule #define IPSTACK_TIMEOUT_CANCEL iplite_timeout_cancel #define Ipstack_timeout_handler Iplite_timeout_handler #define Ipstack_timeout Iplite_timeout #endif #endif /* IPPPP_PPPMP_FRAGMENT_TIMER */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ #ifdef IPPPP_PPPMP_FRAGMENT_TIMER /* * Stucture to support list of scheduled timeouts, * so that they can be cancelled when either they complete or * an error causes them to be cleaned up */ struct Ipppp_pppmp_tmo { Ipcom_list list; /* List type is first element */ Ip_u32 seqnum; /* Sequence number of beginning fragment */ void *handle; /* Handle for referencing the timeout */ Ipppp_pppmp_pkt_frags *pkt_frags; /* Ring of fragments */ }; #endif /* IPPPP_PPPMP_FRAGMENT_TIMER */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ IP_EXTERN Ipppp_data ipppp; IP_EXTERN Ip_u16 ipppp_fcstab[]; /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_STATIC int ipppp_pppmp_bundle_min_mtu (Ipppp_netif *pppif); IP_STATIC int ipppp_pppmp_epd_compare (Ipppp_netif * pppif, Ipppp_link * link); IP_STATIC Ipcom_netif * ipppp_pppmp_epd_search (Ipppp_link * link); IP_STATIC void ipppp_pppmp_free_fragments(Ipcom_netif * netif, Ipppp_pppmp_pkt_frags *pkt_frags); IP_STATIC Ipcom_pkt * ipppp_pppmp_reconstruct_pkt(Ipcom_netif * netif, Ipppp_pppmp_pkt_frags *pkt_frags, int slot); IP_STATIC int ipppp_pppmp_bundle_link_add(Ipcom_netif *netif, Ipppp_link *link); IP_STATIC int ipppp_pppmp_member_link_add(Ipcom_netif *netif, Ipppp_link *link); IP_STATIC int ipppp_pppmp_input_pkt(Ipcom_netif *netif, Ipcom_pkt *pkt); IP_STATIC int ipppp_pppmp_output_fragment(Ipcom_netif *netif, Ipcom_pkt *pkt, Ipppp_link *link, Ip_u16 protocol); #ifdef IPPPP_PPPMP_FRAGMENT_TIMER IP_STATIC Ip_err ipppp_pppmp_tmo_listinit (void *cookie); IP_STATIC void ipppp_pppmp_tmo_handler(void *cookie); IP_STATIC int ipppp_pppmp_tmo_schedule (struct Ipppp_pppmp_tmo *tmo, Ipppp_netif *pppif); IP_STATIC int ipppp_pppmp_tmo_cancel (Ip_u32 seqnum, Ipppp_netif *pppif); IP_STATIC int ipppp_pppmp_tmo_cleanup (Ipppp_netif *pppif); #endif /* IPPPP_PPPMP_FRAGMENT_TIMER */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ #ifdef IPPPP_PPPMP_FRAGMENT_TIMER /* *=========================================================================== * ipppp_pppmp_tmo_listinit *=========================================================================== * Description: Initialize the fragment timeout list, must be done before * the list can be used. * Parameters: A cookie (a pointer to the PPP interface, which has * the timeout list). * Returns: IPCOM_SUCCESS (only). * */ IP_STATIC Ip_err ipppp_pppmp_tmo_listinit (void *cookie) { Ipppp_netif *pppif = (Ipppp_netif *)cookie; ipcom_list_init(&pppif->data.mp.tmo_list); return IPCOM_SUCCESS; } /* *=========================================================================== * ipppp_pppmp_tmo_handler *=========================================================================== * Description: Fragmentation timeout handler. Should never be called under * normal circumstances, only if the packet fails to be * reconstructed in a reasonable amount of time. * Parameters: Pointer to the timeout number, which is the slot or sequence * number of the first fragment of the failed packet. * Returns: None. * */ IP_STATIC void ipppp_pppmp_tmo_handler(void *cookie) { struct Ipppp_pppmp_tmo *tmo = (struct Ipppp_pppmp_tmo *)cookie; Ipppp_pppmp_pkt_frags *pkt_frags = tmo->pkt_frags; Ipcom_list *list_entry = (Ipcom_list *)cookie; Ip_u32 seqnum = tmo->seqnum; Ip_u32 i; for (i = seqnum; i < seqnum+IPPPP_PPPMP_MAX_FRAGMENTS; i++) { Ip_u32 numslots = IPPPP_PPPMP_MAX_FRAGMENTS*2; Ip_u32 slot = i % numslots; if (pkt_frags->frags[slot] != IP_NULL) { Ipcom_pkt *pkt = pkt_frags->frags[slot]; if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_ALLOC)) ipcom_pkt_free(pkt_frags->frags[slot]); pkt_frags->frags[slot] = IP_NULL; } /* Check for either that this is a last fragment (of this pkt) or that the next frag is the beginning of another pkt */ if (IP_BIT_ISSET(pkt_frags->flags[slot], IPPPP_MPHEADER_LAST_FRAGMENT) || IP_BIT_ISSET(pkt_frags->flags[slot+1 % numslots], IPPPP_MPHEADER_FIRST_FRAGMENT)) break; } ipcom_list_remove(list_entry); ipcom_free(tmo); return; } /* *=========================================================================== * ipppp_pppmp_tmo_schedule *=========================================================================== * Description: Schedule a packet fragmentation timeout. * Parameters: - An initialized timeout object containing information about * the packet being reconstructed. * - The PPP interface, which has the timeout list. * Returns: 0 if OK, -1 otherwise. * */ IP_STATIC int ipppp_pppmp_tmo_schedule (struct Ipppp_pppmp_tmo *tmo, Ipppp_netif *pppif) { /* Initialize the list, if needed */ if (ipcom_once(&pppif->data.mp.tmo_list_once, ipppp_pppmp_tmo_listinit, (void *)pppif) != IPCOM_SUCCESS) { IP_PANIC(); return -1; } /* Add the timeout to the list */ ipcom_list_insert_last(&pppif->data.mp.tmo_list, &tmo->list); /* Schedule a timeout in 20 sec. */ (void)IPSTACK_TIMEOUT_SCHEDULE(IPPPP_PPPMP_TIMEOUT_LENGTH, (Ipstack_timeout_handler)ipppp_pppmp_tmo_handler, (void *) tmo, (Ipstack_timeout **)&tmo->handle); return 0; } /* *=========================================================================== * ipppp_pppmp_tmo_cancel *=========================================================================== * Description: Cancel a frgmentation timer. * Parameters: - The sequence/slot number of the first fragment of the * packet, which identifies the timer to cancel. * - The PPP interface, which has the timeout list. * Returns: 0 if OK, -1 otherwise. * */ IP_STATIC int ipppp_pppmp_tmo_cancel (Ip_u32 seqnum, Ipppp_netif *pppif) { struct Ipppp_pppmp_tmo *tmo; Ipcom_list *list_entry; /* Shouldn't ever cancel before scheduling, but just in case */ if (ipcom_once(&pppif->data.mp.tmo_list_once, ipppp_pppmp_tmo_listinit, (void *)pppif) != IPCOM_SUCCESS) { IP_PANIC(); return -1; } list_entry = ipcom_list_first(&pppif->data.mp.tmo_list); while (list_entry) { tmo = (struct Ipppp_pppmp_tmo *)list_entry; if (tmo->seqnum == seqnum) { ipcom_list_remove (list_entry); IPSTACK_TIMEOUT_CANCEL (tmo->handle); ipcom_free(tmo); return 0; } list_entry = ipcom_list_next(list_entry); } return -1; } /* *=========================================================================== * ipppp_pppmp_tmo_cleanup *=========================================================================== * Description: Cancel all timers and remove from list all timers. * Called when an interface is taken down, as part of * pppmp releated cleanup. * * Parameters: The PPP interface, which has the timeout list. * Returns: 0 if OK, -1 otherwise. * */ IP_STATIC int ipppp_pppmp_tmo_cleanup (Ipppp_netif *pppif) { Ipcom_list *list_entry; /* Shouldn't ever clean before scheduling, but just in case */ if (ipcom_once(&pppif->data.mp.tmo_list_once, ipppp_pppmp_tmo_listinit, (void *)pppif) != IPCOM_SUCCESS) { IP_PANIC(); return -1; } list_entry = ipcom_list_first(&pppif->data.mp.tmo_list); while (list_entry) { struct Ipppp_pppmp_tmo *tmo = (struct Ipppp_pppmp_tmo *)list_entry; Ipcom_list *next_entry = ipcom_list_next(list_entry); ipcom_list_remove (list_entry); IPSTACK_TIMEOUT_CANCEL (tmo->handle); ipcom_free(tmo); list_entry = next_entry; } return 0; } #endif /* IPPPP_PPPMP_FRAGMENT_TIMER */ /* *=========================================================================== * ipppp_pppmp_bundle_min_mtu *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipppp_pppmp_bundle_min_mtu (Ipppp_netif *pppif) { Ipppp_link *link; int i, this_mtu; int minsize = IPPPP_PPPMP_MRRU_MAX; if (IP_BIT_ISFALSE(pppif->pppflags, IPPPP_PPPIFFLAG_MP_BUNDLE)) return -1; for (i = 0; i < IPPPP_MAX_LINKS; i++) { if (((link = &pppif->link[i]) != IP_NULL) && (IP_BIT_ISSET(link->data.flags, IPPPP_LINKFLAG_UP))) { if (IP_BIT_ISSET(link->ppp_flags, IP_PPP_FLAG_MP_SHADOW)) { Ipcom_netif *member_netif = link->member_netif; Ipppp_netif *member_pppif = member_netif->plink; Ipppp_link *member_link = &member_pppif->link[link->member_link_index]; this_mtu = member_link->drv.mtu; if (member_netif->type == IP_IFT_PPPOE) this_mtu -= (IPPPP_PPPOE_HDR_SIZE + IPPPP_ETH_HDR_SIZE + (IPPPP_PPPMP_HDR_SIZE - (member_link->drv.ssn ? 2 : 0)) ); else this_mtu -= (IPPPP_PPPMP_HDR_SIZE - (member_link->drv.ssn ? 2 : 0)); } else { Ipcom_netif *bundle_netif = link->bundle_netif; this_mtu = link->drv.mtu; if ((bundle_netif) && (bundle_netif->type == IP_IFT_PPPOE)) this_mtu -= (IPPPP_PPPOE_HDR_SIZE + IPPPP_ETH_HDR_SIZE + (IPPPP_PPPMP_HDR_SIZE - (link->drv.ssn ? 2 : 0)) ); else this_mtu -= (IPPPP_PPPMP_HDR_SIZE - (link->drv.ssn ? 2 : 0)); } if ((this_mtu > 0) && (this_mtu < minsize)) minsize = this_mtu; } } pppif->data.mp.min_mtu = minsize; return minsize; } /* *=========================================================================== * ipppp_pppmp_epd_compare *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipppp_pppmp_epd_compare (Ipppp_netif * pppif, Ipppp_link * link) { Ip_u8 epd_class = link->drv.peer_epd_class; Ip_u8 *epd_data = link->drv.peer_epd_data; Ip_u8 epd_datalen = link->drv.peer_epd_datalen; if ((epd_class == pppif->data.mp.peer_epd_class) && (epd_datalen == pppif->data.mp.peer_epd_datalen) && (ipcom_memcmp(epd_data, pppif->data.mp.peer_epd_data, pppif->data.mp.peer_epd_datalen) == 0)) return 0; else return -1; } /* *=========================================================================== * ipppp_pppmp_epd_search *=========================================================================== * Description: * Find out if a bundle exists with the peer identified in the link. * Parameters: * Returns: * */ IP_STATIC Ipcom_netif * ipppp_pppmp_epd_search (Ipppp_link * link) { Ipcom_netif * netif = IP_NULL; Ipppp_netif * pppif; Ip_u32 i; for (i = 0; i < ipnet->netifs->size; i++, netif = IP_NULL) { if ( ((netif = (Ipcom_netif *) ipnet->netifs->table[i]) != IP_NULL) && ((netif->type == IP_IFT_PPP) || (netif->type == IP_IFT_PPPOE) || (netif->type == IP_IFT_PPPL2TP)) && ((pppif = (Ipppp_netif *) netif->plink) != IP_NULL) && (IP_BIT_ISSET(pppif->pppflags, IPPPP_PPPIFFLAG_MP_BUNDLE)) && (ipppp_pppmp_epd_compare (pppif, link) == 0) ) { /* This here is a bundle that this link should be joined to */ return (netif); } } return IP_NULL; } /* *=========================================================================== * ipppp_pppmp_free_fragments *=========================================================================== * Description: * * Parameters: * Return Codes: * */ IP_STATIC void ipppp_pppmp_free_fragments(Ipcom_netif * netif, Ipppp_pppmp_pkt_frags *pkt_frags) { int i; for (i = 0; i < IPPPP_PPPMP_MAX_FRAGMENTS*2; i++) { if (pkt_frags->frags[i] != IP_NULL) { Ipcom_pkt *pkt = pkt_frags->frags[i]; if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_ALLOC)) ipcom_pkt_free(pkt_frags->frags[i]); } } } /* *=========================================================================== * ipppp_pppmp_reconstruct_pkt *=========================================================================== * Description: Create a new packet from received fragments * * Parameters: * Return Codes: A valid reconstructed packet, or IP_NULL if not ready. * */ IP_STATIC Ipcom_pkt * ipppp_pppmp_reconstruct_pkt(Ipcom_netif * netif, Ipppp_pppmp_pkt_frags *pkt_frags, int slot) { int i; int numslots = IPPPP_PPPMP_MAX_FRAGMENTS*2; int first = -1; int last = -1; int startsearch; int tryslot; int numfrags; Ipcom_pkt * pkt; Ipcom_pkt * new_pkt; Ip_size_t pkt_size; Ip_size_t data_size; Ipppp_netif *pppif = netif->plink; int ssn = pppif->data.mp.ssn; /* netif parameter here is always bundle netif */ /* Search for pkt with FIRST flag */ if (IP_BIT_ISSET(pkt_frags->flags[slot], IPPPP_MPHEADER_FIRST_FRAGMENT)) first = slot; else { /* Offset the starting index to transparently decrement through circular buffer */ for (startsearch = slot+numslots, i = 0; i < IPPPP_PPPMP_MAX_FRAGMENTS; i++) { tryslot = (startsearch-i) % numslots; if (pkt_frags->frags[tryslot] == IP_NULL) return (IP_NULL); else if (IP_BIT_ISSET(pkt_frags->flags[tryslot], IPPPP_MPHEADER_FIRST_FRAGMENT)) { first = tryslot; break; } } } /* If no first found, not ready to reassemble */ if (first < 0) return (IP_NULL); /* Search for pkt with LAST flag */ if (IP_BIT_ISSET(pkt_frags->flags[slot], IPPPP_MPHEADER_LAST_FRAGMENT)) last = slot; else { for (startsearch = slot, i = 0; i < IPPPP_PPPMP_MAX_FRAGMENTS; i++) { tryslot = (startsearch+i) % numslots; if (pkt_frags->frags[tryslot] == IP_NULL) break; /* don't return, may need to schedule a timer */ else if (IP_BIT_ISSET(pkt_frags->flags[tryslot], IPPPP_MPHEADER_LAST_FRAGMENT)) { last = tryslot; break; } } } /* If no last found, not ready to reassemble */ if (last < 0) { #ifdef IPPPP_PPPMP_FRAGMENT_TIMER if (slot == first) { struct Ipppp_pppmp_tmo *tmo = ipcom_malloc(sizeof(struct Ipppp_pppmp_tmo)); /* If this is the first fragment in a multi-fragment packet, schedule a timer to clean up if a fragment is lost */ if (tmo) { tmo->seqnum = first; tmo->pkt_frags = pkt_frags; ipppp_pppmp_tmo_schedule(tmo, pppif); } } #endif return (IP_NULL); } /* * Calculate the number of fragments, taking into account that * there may have been a wrap-around, */ if (first > last) numfrags = (last + numslots) - first + 1; else numfrags = last - first + 1; /* * Add up the size of all fragements, * (subtract size of ML header from each) */ for (pkt_size = 0, i = 0; i < numfrags; i++) { tryslot = (first+i) % numslots; pkt = pkt_frags->frags[tryslot]; pkt_size += (Ip_size_t) (pkt->end - pkt->start) - (ssn ? 4 : 6); } /* Make the new pkt size no bigger than the mru + header size */ if ((Ip_u16)pkt_size > pppif->data.mp.local_mrru + IPPPP_HDR_SIZE) { IPPPP_STATS(input_err_long++); ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, "Received packet exceeds MRRU, dropping"); for (i = 0; i < numfrags; i++) { tryslot = (first+i) % numslots; pkt = pkt_frags->frags[tryslot]; ipcom_pkt_free(pkt); pkt_frags->frags[tryslot] = IP_NULL; } return (IP_NULL); } /* Make it no smaller that the standard packet size */ pkt_size = IP_MAX(IPPPP_PKTSIZE, pkt_size); /* Allocate a new packet with that much space */ new_pkt = ipcom_pkt_malloc(pkt_size, IP_FLAG_FC_STACKCONTEXT); if (new_pkt == IP_NULL) { IPPPP_STATS(input_err_esc_enomem++); ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, "Out of memory for new packet"); return (IP_NULL); } new_pkt->ifindex = netif->ifindex; new_pkt->link_cookie = netif->link_index; new_pkt->end = new_pkt->start = ipcom_conf_max_link_hdr_size; /* * Go through the fragements one by one, * copying into the new packet, * then copy in the last packet */ for (i = 0; i < numfrags; i++) { tryslot = (first+i) % numslots; pkt = pkt_frags->frags[tryslot]; data_size = (Ip_size_t)(pkt->end - pkt->start) - (ssn ? 4 : 6); ipcom_memcpy(&new_pkt->data[new_pkt->end], &pkt->data[pkt->start + (ssn ? 4 : 6)], data_size); new_pkt->end += (int)data_size; ipcom_pkt_free(pkt); pkt_frags->frags[tryslot] = IP_NULL; } #ifdef IPPPP_PPPMP_FRAGMENT_TIMER /* Cancel the reconstruction timeout for this packet */ ipppp_pppmp_tmo_cancel(first, pppif); #endif return (new_pkt); } /* *=========================================================================== * ipppp_pppmp_bundle_link_add *=========================================================================== * Description: * Parameters: * Returns: 0 if link added, otherwise -1. * */ IP_STATIC int ipppp_pppmp_bundle_link_add(Ipcom_netif *netif, Ipppp_link *link) { Ipppp_netif *pppif = netif->plink; Ipppp_phase *phase = IP_NULL; Ipppp_link *link0; Ipppp_phase *phase0; /* Only PPPoE interfaces can bring up multiple links */ if (netif->type != IP_IFT_PPPOE) return -1; /* This is not the first link in the bundle. */ /* No further negotiation needs to be done */ pppif->data.mp.open_links++; /* Check that the peer has the matching endpoint discriminator */ if (ipppp_pppmp_epd_compare (pppif, link) != 0) { ipppp_log(IPCOM_LOG_INFO, netif, IP_NULL, "link %d endpoint does not match", link->link_index); /* Go ahead with link bring up */ return -1; } /* Check that this link has negotiated a matching MRRU */ if (link->drv.peer_mrru != pppif->data.mp.peer_mrru) { ipppp_log(IPCOM_LOG_INFO, netif, IP_NULL, "link %d MRRU does not match", link->link_index); /* Go ahead with link bring up */ return -1; } /* Use link 0 as a template for the other links */ link0 = &pppif->link[0]; /* Check that this link has negotiated a matching Short Sequence Number option */ if (link->drv.ssn != link0->drv.ssn) { ipppp_log(IPCOM_LOG_INFO, netif, phase, "link %d Short Sequence Number option does not match", link->link_index); /* Go ahead with link bring up */ return -1; } phase0 = &link0->phases[IPPPP_PHASE_IPCP]; phase = &link->phases[IPPPP_PHASE_IPCP]; phase->state = phase0->state; phase0 = &link0->phases[IPPPP_PHASE_IPV6CP]; phase = &link->phases[IPPPP_PHASE_IPV6CP]; phase->state = phase0->state; link->bundle_netif = netif; ipppp_pppmp_bundle_min_mtu(pppif); ipppp_log(IPCOM_LOG_INFO, netif, IP_NULL, "link %d added to bundle", link->link_index); /* See if there are any more links to bring up */ if (pppif->data.mp.open_links < pppif->data.mp.mp_links) { if (ipppp_pppmp_open (netif, pppif->data.mp.open_links) < 0) ipppp_log(IPCOM_LOG_DEBUG, netif, phase, "link open failed"); } return 0; } /* *=========================================================================== * ipppp_pppmp_member_link_add *=========================================================================== * Description: * Parameters: * Returns: 0 if link add has been handled, otherwise -1. * */ IP_STATIC int ipppp_pppmp_member_link_add(Ipcom_netif *netif, Ipppp_link *link) { Ipppp_netif *pppif = netif->plink; Ipppp_phase *phase; Ipcom_netif *bundle_netif; Ipppp_netif *bundle_pppif; Ipppp_link *bundle_link; Ipppp_link *link0; Ipppp_phase *phase0; Ipppp_phase *bundle_link_phase; /* * Verify that this link matches the bundle peer identification oand MRRU */ if (((bundle_netif = ipppp_pppmp_epd_search(link)) != IP_NULL) && ((bundle_pppif = bundle_netif->plink) != IP_NULL) && ((link0 = &bundle_pppif->link[0]) != IP_NULL) && (link->drv.peer_mrru == link0->drv.peer_mrru) && (link->drv.ssn == link0->drv.ssn)) /* Check, if SSN for member link negotiated the same as master link*/ { int bundle_link_index = -1; int i; /* Allocate a link index in the bundle netif, if possible */ for (i = 0; i < IPPPP_MAX_LINKS; i++) { Ipppp_link * tlink = &bundle_pppif->link[i]; if (IP_BIT_ISFALSE(tlink->data.flags, IPPPP_LINKFLAG_UP)) { bundle_link_index = i; break; } } if (bundle_link_index < 0) { ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, "link add failed: (bundle %s: no links available)", bundle_netif->name); /* Close link */ netif->link_index = link->link_index; ipppp_action_lcp_close(netif, link); /* * This link should have been added to an existing bundle, * returning 0 here means don't proceed with link bring up. */ return 0; } /* Setup input and output redirection to this link */ link->bundle_netif = bundle_netif; link->bundle_link_index = bundle_link_index; link->member_netif = netif; link->member_link_index = link->link_index; IP_BIT_SET(link->ppp_flags, IP_PPP_FLAG_MP_MEMBER); /* Set the shadow link LINKFLAG:UP */ bundle_link = &bundle_pppif->link[bundle_link_index]; bundle_link->member_netif = netif; bundle_link->member_link_index = link->link_index; bundle_link->bundle_netif = bundle_netif; bundle_link->bundle_link_index = bundle_link->link_index; IP_BIT_SET(bundle_link->ppp_flags, IP_PPP_FLAG_MP_SHADOW); /* Make sure the bundle link points to itself so that it can be brought down correctly */ /* This, at least, is used by ipppp_pppmp_output (more neeed) */ bundle_link->drv.mtu = link->drv.mtu; /* Set bundle link LINKFLAG UP */ IP_BIT_SET(bundle_link->data.flags, IPPPP_LINKFLAG_UP); /* Set phase etc and log message */ phase0 = &link0->phases[IPPPP_PHASE_IPCP]; phase = &link->phases[IPPPP_PHASE_IPCP]; bundle_link_phase = &bundle_link->phases[IPPPP_PHASE_IPCP]; phase->state = phase0->state; bundle_link_phase->state = phase0->state; phase0 = &link0->phases[IPPPP_PHASE_IPV6CP]; phase = &link->phases[IPPPP_PHASE_IPV6CP]; bundle_link_phase = &bundle_link->phases[IPPPP_PHASE_IPV6CP]; phase->state = phase0->state; bundle_link_phase->state = phase0->state; /* * Also set the bundle (shadow) link LCP phase to OPENED, * available to recieve packets. */ bundle_link->phases[IPPPP_PHASE_LCP].state = IPPPP_STATE_OPENED; bundle_link->phases[IPPPP_PHASE_AUTH].state = IPPPP_STATE_OPENED; bundle_pppif->data.mp.open_links++; pppif->data.mp.member_links++; IP_BIT_SET(pppif->pppflags, IPPPP_PPPIFFLAG_MP_MEMBER); /* Recalculate bundle min mtu */ ipppp_pppmp_bundle_min_mtu(bundle_pppif); ipppp_log(IPCOM_LOG_INFO, netif, IP_NULL, "member link %d added to bundle %s", link->link_index, bundle_netif->name); return 0; } else return -1; } /* *=========================================================================== * ipppp_pppmp_shadow_link_remove *=========================================================================== * Description: * Parameters: * Returns: 0 if the link is closed as a result of this call, * 1 otherwise (so should continue with action_down) * */ IP_STATIC int ipppp_pppmp_shadow_link_remove(Ipcom_netif *netif, Ipppp_link *link) { Ipppp_netif *pppif = netif->plink; Ipcom_netif *member_netif; Ipppp_netif *member_pppif; Ipppp_link *member_link; /* * This is a shadow link. * Get the real (member) link and close it, if it is UP. * Then clean up the shadow settings and return. */ if (((member_netif = link->member_netif) != IP_NULL) && ((member_pppif = member_netif->plink) != IP_NULL) && ((member_link = &member_pppif->link[link->member_link_index]) != IP_NULL)) { if (member_pppif->data.mp.member_links > 0) member_pppif->data.mp.member_links--; IP_BIT_CLR(member_link->ppp_flags, IP_PPP_FLAG_MP_MEMBER); member_link->bundle_netif = member_link->member_netif = IP_NULL; member_link->phases[IPPPP_PHASE_IPCP].state = IPPPP_STATE_INITIAL; member_link->phases[IPPPP_PHASE_IPV6CP].state = IPPPP_STATE_INITIAL; if (IP_BIT_ISSET(member_link->data.flags, IPPPP_LINKFLAG_UP)) { member_netif->link_index = member_link->link_index; ipppp_event_close(member_netif, member_link, &member_link->phases[IPPPP_PHASE_LCP]); } /* Now the pppoe packet would arrive first for ethernet is generally faster than serial. So if a PPP interface connection is added as a member link to a pppoe bundle, sleep a moment will make the sent termreq packet in PPP tunnel arriving earier at the peer target than pppoe termreq packet in ethernet network as we expect. */ if (member_netif->type == IP_IFT_PPP) ipcom_millisleep(100); } link->member_netif = link->bundle_netif = IP_NULL; IP_BIT_CLR(link->data.flags, IPPPP_LINKFLAG_UP); IP_BIT_CLR(link->ppp_flags, IP_PPP_FLAG_MP_SHADOW); link->phases[IPPPP_PHASE_LCP].state = IPPPP_STATE_INITIAL; link->phases[IPPPP_PHASE_AUTH].state = IPPPP_STATE_INITIAL; link->phases[IPPPP_PHASE_IPCP].state = IPPPP_STATE_INITIAL; link->phases[IPPPP_PHASE_IPV6CP].state = IPPPP_STATE_INITIAL; if (pppif->data.mp.open_links > 0) pppif->data.mp.open_links--; if (pppif->data.mp.open_links == 0) pppif->num_ncp_open = 0; else { Ipppp_link *tlink; int i; ipppp_pppmp_bundle_min_mtu(pppif); if (pppif->data.mp.open_links > 0) { for (i = IPPPP_MAX_LINKS-1; i >= 0; i--) { tlink = &pppif->link[i]; if (IP_BIT_ISSET(tlink->data.flags, IPPPP_LINKFLAG_UP)) { netif->link_index = i; ipppp_event_close(netif, tlink, &tlink->phases[IPPPP_PHASE_LCP]); break; } } } return 0; } return 1; } /* *=========================================================================== * ipppp_pppmp_member_link_remove *=========================================================================== * Description: * Parameters: * Returns: 0 if the link is closed as a result of this call, * 1 otherwise (so should continue with action_down) * */ IP_STATIC int ipppp_pppmp_member_link_remove(Ipcom_netif *netif, Ipppp_link *link) { Ipppp_netif *pppif = netif->plink; Ipcom_netif *bundle_netif; Ipppp_netif *bundle_pppif; Ipppp_link *shadow_link; /* * This is a member link. * Clean up the shadow settings and member settings, go on. */ if (((bundle_netif = link->bundle_netif) != IP_NULL) && ((bundle_pppif = bundle_netif->plink) != IP_NULL) && ((shadow_link = &bundle_pppif->link[link->bundle_link_index]) != IP_NULL)) { shadow_link->member_netif = shadow_link->bundle_netif = IP_NULL; IP_BIT_CLR(shadow_link->ppp_flags, IP_PPP_FLAG_MP_SHADOW); IP_BIT_CLR(shadow_link->data.flags, IPPPP_LINKFLAG_UP); shadow_link->phases[IPPPP_PHASE_LCP].state = IPPPP_STATE_INITIAL; shadow_link->phases[IPPPP_PHASE_AUTH].state = IPPPP_STATE_INITIAL; shadow_link->phases[IPPPP_PHASE_IPCP].state = IPPPP_STATE_INITIAL; shadow_link->phases[IPPPP_PHASE_IPV6CP].state = IPPPP_STATE_INITIAL; if (bundle_pppif->data.mp.open_links > 0) bundle_pppif->data.mp.open_links--; if (bundle_pppif->data.mp.open_links == 0) bundle_pppif->num_ncp_open = 0; else ipppp_pppmp_bundle_min_mtu(bundle_pppif); } link->member_netif = link->bundle_netif = IP_NULL; IP_BIT_CLR(link->ppp_flags, IP_PPP_FLAG_MP_MEMBER); link->phases[IPPPP_PHASE_IPCP].state = IPPPP_STATE_INITIAL; link->phases[IPPPP_PHASE_IPV6CP].state = IPPPP_STATE_INITIAL; if (pppif->data.mp.member_links > 0) pppif->data.mp.member_links--; if (pppif->data.mp.member_links == 0) IP_BIT_CLR(pppif->pppflags, IPPPP_PPPIFFLAG_MP_MEMBER); return 1; } /* *=========================================================================== * ipppp_pppmp_input_pkt *=========================================================================== * Description: Input handler for ethernet frames. pkt->start is the offset * to the ethernet header. * Parameters: pkt - The ethernet frame. * Returns: 0 = success, <0 error code * */ IP_STATIC int ipppp_pppmp_input_pkt(Ipcom_netif *netif, Ipcom_pkt *pkt) { Ipppp_link *link; Ip_u16 protocol, length; Ipppp_netif *pppif = netif->plink; #ifdef IPNET pkt->vr_index = ((Ipnet_netif *)netif)->vr_index; #endif /* Get PPP protocol field. */ protocol = IP_GET_NTOHS(&pkt->data[pkt->start]); pkt->start += 2; link = &pppif->link[netif->link_index]; /* Any non-LCP packets received during LCP phase are silently discarded. RFC1661:p7 */ if (link->phases[IPPPP_PHASE_LCP].state != IPPPP_STATE_OPENED && protocol != IPPPP_PROTOCOL_LCP) { ipppp_log(IPCOM_LOG_DEBUG, netif, &link->phases[IPPPP_PHASE_LCP], "discarding input packet - LCP not up"); IPPPP_STATS(input_lcpdown++); goto discard; } /* Any non-LCP, non-AUTH, non-link quality packets received during initial AUTH phase * are silently discarded. RFC1661:p8 */ #ifdef IPPPP_USE_AUTH if (IPPPP_DOING_AUTH(link) && IP_BIT_ISFALSE(link->data.flags, IPPPP_LINKFLAG_AUTHENTICATED) && protocol != IPPPP_PROTOCOL_LCP && protocol != IPPPP_PROTOCOL_PAP && protocol != IPPPP_PROTOCOL_CHAP) { ipppp_log(IPCOM_LOG_DEBUG, netif, IP_NULL, "discarding input packet - doing AUTH"); IPPPP_STATS(input_authdown++); goto discard; } #endif pkt->fd = -1; /* Act on the protocol. */ switch(protocol) { case IPPPP_PROTOCOL_LCP: #ifdef IPCOM_USE_INET case IPPPP_PROTOCOL_IPCP: #endif #ifdef IPCOM_USE_INET6 case IPPPP_PROTOCOL_IPV6CP: #endif /* Minimum LCP/IPCP header is 4 bytes long. */ if (pkt->end - pkt->start < 4) { IPPPP_STATS(input_err_short++); goto discard; } /* If still in wincompat mode but receive a normal PPP packet, disabled it now. */ if (IP_BIT_ISTRUE(link->data.flags, IPPPP_LINKFLAG_WINCOMPAT)) ipppp_drv_wincompat(netif, link, 0); /* LCP packet length must not be longer than available data in pkt or MTU. RFC1661:p5 */ length = IP_GET_NTOHS(&pkt->data[pkt->start+2]); if (length > pkt->end - pkt->start || length > link->drv.mru) { IPPPP_STATS(input_err_long++); goto discard; } /* Strip any padding bytes. */ pkt->end = pkt->start + (int)length; /* RFC1661:p5 */ if (protocol == IPPPP_PROTOCOL_LCP) { /* LCP code 1-7, use default config in replies. */ if (pkt->data[pkt->start] <= IPPPP_CODE_CODEREJ) IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_DEFCONF); /* Call the protocol input function using LCP phase. */ if (ipppp_prot_input(netif, (Ipcom_pkt *) pkt, link, &link->phases[IPPPP_PHASE_LCP]) != 0) { IPPPP_STATS(input_err_lcp++); goto discard; } } #ifdef IPCOM_USE_INET if (protocol == IPPPP_PROTOCOL_IPCP) { if (pppif->data.ipcp.status == -1) goto protrej; /* Call the protocol input function using IPCP phase. */ if (ipppp_prot_input(netif, (Ipcom_pkt *) pkt, link, &link->phases[IPPPP_PHASE_IPCP]) != 0) { IPPPP_STATS(input_err_ipcp++); goto discard; } } #endif #ifdef IPCOM_USE_INET6 if (protocol == IPPPP_PROTOCOL_IPV6CP) { if (pppif->data.ipv6cp.status == -1) goto protrej; /* Call the protocol input function using IPV6CP phase. */ if (ipppp_prot_input(netif, (Ipcom_pkt *) pkt, link, &link->phases[IPPPP_PHASE_IPV6CP]) != 0) { IPPPP_STATS(input_err_ipv6cp++); goto discard; } } #endif break; #ifdef IPPPP_USE_AUTH_PAP case IPPPP_PROTOCOL_PAP: if (ipppp_pap_input(netif, pkt, link, &link->phases[IPPPP_PHASE_AUTH]) != 0) { IPPPP_STATS(input_err_pap++); goto discard; } break; #endif /* IPPPP_USE_AUTH_PAP */ #ifdef IPPPP_USE_AUTH_CHAP case IPPPP_PROTOCOL_CHAP: if (ipppp_chap_input(netif, pkt, link, &link->phases[IPPPP_PHASE_AUTH]) != 0) { IPPPP_STATS(input_err_chap++); goto discard; } break; #endif /* IPPPP_USE_AUTH_CHAP */ #ifdef IPCOM_USE_INET case IPPPP_PROTOCOL_IPV4: /* Discard all IPv4 packets if not in NCP phase and IPv4 opened. RFC1661:p9 */ if (link->phases[IPPPP_PHASE_IPCP].state != IPPPP_STATE_OPENED || pppif->data.ipcp.status == -1 || IP_BIT_ISFALSE(netif->flags, IP_IFF_UP)) { IPPPP_STATS(input_ipcpdown++); goto discard; } return (int)ipcom_ppp_ioevent(netif, IPPPP_EIOXINPUT_IN, (Ipcom_pkt *) pkt); #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IPPPP_PROTOCOL_IPV6: /* Discard all IPv6 packets if not in NCP phase and IPv6 opened. RFC1661:p9 */ if (link->phases[IPPPP_PHASE_IPV6CP].state != IPPPP_STATE_OPENED || pppif->data.ipv6cp.status == -1 || IP_BIT_ISFALSE(netif->flags, IP_IFF_UP)) { IPPPP_STATS(input_ipv6cpdown++); goto discard; } return (int)ipcom_ppp_ioevent(netif, IPPPP_EIOXINPUT_IN6, (Ipcom_pkt *) pkt); #endif /* IPCOM_USE_INET6 */ default: protrej: { Ipppp_phase *phase; IPPPP_STATS(input_err_noproto++); /*!!netif->noproto++;*/ /* Send protocol reject if LCP is opened. RFC1661:p35 */ phase = &link->phases[IPPPP_PHASE_LCP]; if (phase->state == IPPPP_STATE_OPENED) { Ipcom_pkt *reply_pkt; reply_pkt = ipppp_pkt_malloc(IPPPP_CODE_PROTREJ, ++phase->id, phase); if (reply_pkt != IP_NULL) { length = IP_GET_NTOHS(&pkt->data[pkt->start+2]); length = (Ip_u16)IP_MIN((Ip_u16)(IPPPP_PKTSIZE - IPPPP_HDR_SIZE), length); /* Rejected-Protocol field */ reply_pkt->data[reply_pkt->end++] = pkt->data[pkt->ipstart]; reply_pkt->data[reply_pkt->end++] = pkt->data[pkt->ipstart + 1]; /* Rejected-Information from Information field to end, excluding FCS. */ ipcom_memcpy(&reply_pkt->data[reply_pkt->end], &pkt->data[pkt->start], length); reply_pkt->end += length; length += 6; ip_assert(length <= 255); /* Update length field. */ IP_SET_HTONS(&pkt->data[pkt->start + 2], length); /* Output the PROTREJ. */ (void)ipppp_output(netif, reply_pkt, link); } else { ipppp_log(IPCOM_LOG_WARNING, netif, phase, "fail to send protocol reject - out of memory"); } } } goto discard; } /* switch(protocol) */ return 0; discard: IPPPP_STATS(input_discard++); ipcom_pkt_free((Ipcom_pkt *) pkt); return 0; } /* *=========================================================================== * ipppp_pppmp_output_fragment *=========================================================================== * Description: Queue * Parameters: * Returns: * */ IP_STATIC int ipppp_pppmp_output_fragment(Ipcom_netif *netif, Ipcom_pkt *pkt, Ipppp_link *link, Ip_u16 protocol) { int ret; Ip_u16 fcs; int start; ip_assert(pkt != IP_NULL); ip_assert(pkt->start < pkt->end); ip_assert(pkt->end - pkt->start > 5); IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_PPP_OUTPUT); ip_assert(link != IP_NULL); if (IP_BIT_ISFALSE(netif->flags, IP_IFF_RUNNING) || IP_BIT_ISFALSE(link->data.flags, IPPPP_LINKFLAG_UP)) { IPPPP_STATS(outputd_discard_linkdown++); ipppp_log(IPCOM_LOG_DEBUG2, netif, IP_NULL, "discarding output packet - link down"); ret = -IP_ERRNO_EHOSTDOWN; goto discard; } /* Add address & control octets. */ if (IP_BIT_ISFALSE(link->drv.drv_flags, IP_DRVPPP_FLAG_NOACFC)) { if (IP_BIT_ISFALSE(link->data.flags, IPPPP_LINKFLAG_DONTSNDAC) || IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_DEFCONF)) { pkt->start--; pkt->data[pkt->start] = IPPPP_CONTROL; pkt->start--; pkt->data[pkt->start] = IPPPP_ADDRESS; } } /* Needs byte-escaping. */ fcs = IPPPP_INITFCS16; if (IP_BIT_ISFALSE(link->drv.drv_flags, IP_DRVPPP_FLAG_NOESCAPE)) { Ipcom_pkt *new_pkt; Ip_size_t pktsize; Ip_u32 snd_accm; Ip_u8 octet; pktsize = ((pkt->end - pkt->start + 2 /* 2 bytes FCS */) << 1) + 2 /* 2 bytes 0x7e */; ip_assert(pktsize <= 3014); new_pkt = ipcom_pkt_malloc(pktsize, IP_FLAG_FC_STACKCONTEXT); if (new_pkt == IP_NULL && ipcom_ppp_ioevent(netif, IPPPP_EIOXBLOCKOK, IP_NULL)) { /* Ok for user processes to block and wait for more packet memory. */ new_pkt = ipcom_pkt_malloc(pktsize, IP_FLAG_FC_STACKCONTEXT|IP_FLAG_FC_BLOCKOK); } if (new_pkt == IP_NULL) { ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, "discarding output packet - out of memory for new packet"); ret = 0; /* Temporary error, packet is lost. */ goto discard; } if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_DEFCONF)) snd_accm = link->def_drv.snd_accm; else snd_accm = link->drv.snd_accm; IPCOM_PKT_TRACE(new_pkt, IPCOM_GET_PKT_ID(pkt)); new_pkt->fd = pkt->fd; new_pkt->end = new_pkt->start = ipcom_conf_max_link_hdr_size; /* Append Terminator octet. */ if (IP_BIT_ISFALSE(link->drv.drv_flags, IP_DRVPPP_FLAG_NOFLAG)) new_pkt->data[new_pkt->end++] = IPPPP_TERMINATOR; /* Escape data. */ for (start = pkt->start; start < pkt->end; start++, new_pkt->end++) { ip_assert(new_pkt->end < new_pkt->maxlen); octet = pkt->data[start]; fcs = (Ip_u16)(fcs >> 8) ^ ipppp_fcstab[(fcs ^ octet) & 0xff]; if (IPPPP_MUST_ESCAPE_SND(octet)) { new_pkt->data[new_pkt->end++] = IPPPP_ESCAPE; octet = octet ^ 0x20; } new_pkt->data[new_pkt->end] = octet; } /* Now add FCS (and escape it) if required */ if (IP_BIT_ISFALSE(link->drv.drv_flags, IP_DRVPPP_FLAG_NOFCS)) { fcs ^= 0xffff; octet = (Ip_u8)(fcs & 0xff); if (IPPPP_MUST_ESCAPE_SND(octet)) { new_pkt->data[new_pkt->end++] = IPPPP_ESCAPE; octet = octet ^ 0x20; } new_pkt->data[new_pkt->end++] = octet; octet = (Ip_u8)(fcs >> 8); if (IPPPP_MUST_ESCAPE_SND(octet)) { new_pkt->data[new_pkt->end++] = IPPPP_ESCAPE; octet = octet ^ 0x20; } new_pkt->data[new_pkt->end++] = octet; } /* Prepend Terminator octet. */ if (IP_BIT_ISFALSE(link->drv.drv_flags, IP_DRVPPP_FLAG_NOFLAG)) new_pkt->data[new_pkt->end++] = IPPPP_TERMINATOR; /* Save 'pkt' for later free or release immediately. */ if (protocol == IPPPP_PROTOCOL_IPV4 || protocol == IPPPP_PROTOCOL_IPV6) { new_pkt->next_original = pkt; IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_PPP_ORGOUT); } else { ip_assert(new_pkt->next_original == IP_NULL); IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_INIT); ipcom_pkt_free(pkt); } pkt = new_pkt; } /* Not byte escaped, but still may need FCS. */ else if (IP_BIT_ISFALSE(link->drv.drv_flags, IP_DRVPPP_FLAG_NOFCS)) { for (start = pkt->start; start < pkt->end; start++) fcs = (Ip_u16)((fcs >> 8) ^ ipppp_fcstab[(fcs ^ pkt->data[start]) & 0xff]); fcs ^= 0xffff; pkt->data[pkt->end++] = (Ip_u8)(fcs & 0xff); pkt->data[pkt->end++] = (Ip_u8)(fcs >> 8); } /* needed? */ netif->link_index = link->link_index; /* Output packet to driver. */ pkt->ifindex = netif->ifindex; pkt->link_cookie = link->link_index; ret = (int)ipcom_ppp_ioevent(netif, IPPPP_EIOXOUTPUT_IF, pkt); ip_assert(ret <= 0); if (ret < 0) { IPPPP_STATS(outputd_discard_drvfail++); ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, "discarding output packet - driver failure : %s", ipcom_strerror(-ret)); } return ret; discard: ip_assert(ret <= 0); ipcom_pkt_free(pkt); return ret; } /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipppp_pppmp_epd_init *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipppp_pppmp_epd_init (Ip_u8 * epd_class, Ip_u8 * epd_data, Ip_u8 * epd_datalen) { Ipcom_netif * netif = IP_NULL; Ip_ssize_t x; Ip_u32 i; ipcom_memset (epd_data, 0, IPPPPMP_EPD_DATA_MAX); *epd_class = IPPPPMP_EPD_CLASS_NULL; *epd_datalen = 0; for (i = 0; i < ipnet->netifs->size; i++, netif = IP_NULL) { if ( ((netif = (Ipcom_netif *) ipnet->netifs->table[i]) != IP_NULL) && (netif->type == IP_IFT_ETHER) && (netif->link_addr_size > 0) ) { for (x = 0; x < netif->link_addr_size; x++) epd_data[x] = ((Ip_u8) netif->link_addr[x]); *epd_class = IPPPPMP_EPD_CLASS_MAC; *epd_datalen = 6; break; } } return; } /* *=========================================================================== * ipppp_pppmp_ifclean *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipppp_pppmp_ifclean(Ipcom_netif *netif) { Ipppp_netif *pppif = netif->plink; Ipppp_link *tlink; int i; IP_BIT_CLR(pppif->pppflags, IPPPP_PPPIFFLAG_MP_MEMBER); IP_BIT_CLR(pppif->pppflags, IPPPP_PPPIFFLAG_MP_BUNDLE); for (i = 0; i < IPPPP_MAX_LINKS; i++) { tlink = &pppif->link[i]; IP_BIT_CLR(tlink->data.flags, IPPPP_LINKFLAG_DONTNEG_MRRU); tlink->data.pppoe_session_id = 0; } /* * If pkt fragments are attached to this interface, * release any orphan fragments and free fragment structure. */ if (pppif->data.mp.pkt_frags != IP_NULL) { ipppp_pppmp_free_fragments(netif, pppif->data.mp.pkt_frags); ipcom_free(pppif->data.mp.pkt_frags); pppif->data.mp.pkt_frags = IP_NULL; } #ifdef IPPPP_PPPMP_FRAGMENT_TIMER /* Removes all timers in list */ ipppp_pppmp_tmo_cleanup (pppif); #endif } /* *=========================================================================== * ipppp_pppmp_open *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL int ipppp_pppmp_open(Ipcom_netif *netif, int link_index) { Ipppp_netif *pppif = netif->plink; Ipppp_link *link; /* * Get the link (index of next link is equal to number of already open links) */ link = &pppif->link[link_index]; netif->link_index = link_index; /* * Also, PPPoE links other than link[0] need to have their flags initialized */ if ((link_index > 0) && (netif->type == IP_IFT_PPPOE)) link->ppp_flags |= (IP_PPP_FLAG_NOACCOMP | IP_PPP_FLAG_NOACCM); /* * Open the link */ return (ipppp_event_open(netif, link, &link->phases[IPPPP_PHASE_LCP])); } /* *=========================================================================== * ipppp_pppmp_link_add *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL int ipppp_pppmp_link_add(Ipcom_netif *netif, Ipppp_link *link) { Ipppp_netif *pppif = netif->plink; /* * If this interface is a bundle, try to add the link; * Otherwise look for an existing bundle on a different interface. */ if (IP_BIT_ISSET(pppif->pppflags, IPPPP_PPPIFFLAG_MP_BUNDLE)) return (ipppp_pppmp_bundle_link_add(netif, link)); else return (ipppp_pppmp_member_link_add(netif, link)); } /* *=========================================================================== * ipppp_pppmp_link_remove *=========================================================================== * Description: * Parameters: * Returns: 0 if the link is closed as a result of this call, * 1 otherwise (so should continue with action_down) * */ IP_GLOBAL int ipppp_pppmp_link_remove(Ipcom_netif *netif, Ipppp_link *link) { Ipppp_netif *pppif = netif->plink; if (IP_BIT_ISSET(link->ppp_flags, IP_PPP_FLAG_MP_SHADOW)) return (ipppp_pppmp_shadow_link_remove(netif, link)); else if (IP_BIT_ISSET(link->ppp_flags, IP_PPP_FLAG_MP_MEMBER)) return (ipppp_pppmp_member_link_remove(netif, link)); else if (pppif->data.mp.open_links > 0) { if (pppif->data.mp.open_links > 1) { pppif->data.mp.open_links--; link->phases[IPPPP_PHASE_LCP].state = IPPPP_STATE_CLOSING; ipppp_action_irc(netif, &link->phases[IPPPP_PHASE_LCP], IPPPP_TIMER_TERMREQ); ipppp_termreq_output(netif, &link->phases[IPPPP_PHASE_LCP], link); } else if (pppif->data.mp.open_links == 1) { pppif->data.mp.open_links--; pppif->num_ncp_open = 0; } } return 1; } /* *=========================================================================== * ipppp_pppmp_input *=========================================================================== * Description: Check out packet to see how ipppp_input should handle it. * Parameters: * Returns: * */ IP_GLOBAL int ipppp_pppmp_input(Ipcom_netif *netif, Ipcom_pkt *pkt) { Ip_u16 protocol; Ip_u16 eprotocol; Ip_u16 flags; Ip_u32 sequence; Ip_u8 code; Ipppp_netif * pppif = netif->plink; Ipcom_pkt * new_pkt; Ipppp_pppmp_pkt_frags * pkt_frags; int numslots; int slot; Ipppp_link * link = &pppif->link[netif->link_index]; Ip_u8 ssn = link->drv.ssn; /* Sanity check - if it fails I can't do anything */ if ((pkt->start >= pkt->maxlen) || (pkt->end > pkt->maxlen)) goto discard; /* Verify that this packet does have a ML header */ protocol = IP_GET_NTOHS(&pkt->data[pkt->start]); if (protocol != IPPPP_PROTOCOL_MP) { ipppp_log(IPCOM_LOG_DEBUG, netif, IP_NULL, "ipppp_multilink_input: not multilink: discarding"); goto discard; } /* This is a multilink encapsulated packet. */ /* Retrieve MP flags and sequence number */ flags = (Ip_u16)((IP_GET_NTOHS(&pkt->data[pkt->start + 2])) & 0xC000); if (ssn) sequence = (IP_GET_NTOHS(&pkt->data[pkt->start + 2])) & 0x0FFF; else sequence = (((IP_GET_NTOHS(&pkt->data[pkt->start + 2])) & 0x00FF) << 16 ) + (IP_GET_NTOHS(&pkt->data[pkt->start + 4])) ; if (IPPPP_MPFRAGMENT_ISONLY(flags)) { /* Strip off the Multilink header */ new_pkt = pkt; new_pkt->start += (ssn ? 4 : 6); } else { Ipcom_netif * reconstruct_netif=IP_NULL; Ipppp_netif * reconstruct_pppif=IP_NULL; /* * If this is a member interface get the bundle; * otherwise, this is the bundle. */ if (IP_BIT_ISSET(pppif->pppflags, IPPPP_PPPIFFLAG_MP_MEMBER)) { Ipcom_netif *bundle_netif = link->bundle_netif; if (IP_BIT_ISFALSE(link->ppp_flags, IP_PPP_FLAG_MP_MEMBER) || (bundle_netif == IP_NULL)) { ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, " Member interface %s, link %d should be a member but is not", netif->name, link->link_index); /* Probably the packet came on an interface that is not ready yet, just drop it. */ goto discard; } else { reconstruct_netif = bundle_netif; reconstruct_pppif = bundle_netif->plink; } } else { reconstruct_netif = netif; reconstruct_pppif = pppif; } /* Alloc pkt fragment holder for this netif, if there isn't already one. */ if (reconstruct_pppif->data.mp.pkt_frags == IP_NULL) { pkt_frags = ipcom_malloc(sizeof(Ipppp_pppmp_pkt_frags)); if (pkt_frags == IP_NULL) { IPPPP_STATS(input_err_esc_enomem++); ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, "discarding input packet - out of memory for multilink fragments"); goto discard; } ipcom_memset(pkt_frags, 0, sizeof(Ipppp_pppmp_pkt_frags)); reconstruct_pppif->data.mp.pkt_frags = pkt_frags; } else pkt_frags = reconstruct_pppif->data.mp.pkt_frags; /* NOT NEEDED if ONLY!!! */ numslots = IPPPP_PPPMP_MAX_FRAGMENTS*2; slot = sequence % numslots; /* We've got the slot number, * but better check that there isn't an orphan fragment left from previous cycle. */ if (pkt_frags->frags[slot] != IP_NULL) { Ipcom_pkt *lostpkt = pkt_frags->frags[slot]; #ifdef IPPPP_PPPMP_FRAGMENT_TIMER if (IP_BIT_ISSET(pkt_frags->flags[slot], IPPPP_MPHEADER_FIRST_FRAGMENT)) ipppp_pppmp_tmo_cancel(slot, reconstruct_pppif); #endif if (IP_BIT_ISSET(lostpkt->flags, IPCOM_PKT_FLAG_ALLOC)) ipcom_pkt_free(lostpkt); pkt_frags->frags[slot] = IP_NULL; ipppp_log(IPCOM_LOG_DEBUG, netif, IP_NULL, "Unused multilink fragment discarded"); } pkt_frags->frags[slot] = pkt; pkt_frags->flags[slot] = flags; new_pkt = ipppp_pppmp_reconstruct_pkt(reconstruct_netif, pkt_frags, slot); if (new_pkt == IP_NULL) return (0); } /* * From here on, only refer to new_pkt, which may be the received packet * if it is the only fragment, or may be the reconstructed packet if * there were multiple fragments. */ /* Determine the encapsulated protocol type */ /* NOTE: ASSUMES LONG SEQUENCE NUMBER */ if (new_pkt->data[new_pkt->start] == 0x21) { eprotocol = IPPPP_PROTOCOL_IPV4; new_pkt->start--; IP_SET_HTONS(&new_pkt->data[new_pkt->start], IPPPP_PROTOCOL_IPV4); } else if (new_pkt->data[new_pkt->start] == 0x57) { eprotocol = IPPPP_PROTOCOL_IPV6; new_pkt->start--; IP_SET_HTONS(&new_pkt->data[new_pkt->start], IPPPP_PROTOCOL_IPV6); } else eprotocol = IP_GET_NTOHS(&new_pkt->data[new_pkt->start]); switch (eprotocol) { case IPPPP_PROTOCOL_IPCP: case IPPPP_PROTOCOL_IPV6CP: /* These are OK. */ /* Process with the same netif it came from */ return (ipppp_pppmp_input_pkt(netif, new_pkt)); case IPPPP_PROTOCOL_PAP: case IPPPP_PROTOCOL_CHAP: case IPPPP_PROTOCOL_CCP: ipppp_log(IPCOM_LOG_DEBUG, netif, IP_NULL, "ipppp_multilink_input: multilink pkt but wrong protocol type: 0x%04x", eprotocol); goto discard; case IPPPP_PROTOCOL_LCP: code = new_pkt->data[new_pkt->start+2]; switch (code) { case IPPPP_CODE_CONFREJ: case IPPPP_CODE_PROTREJ: case IPPPP_CODE_ECHOREQ: case IPPPP_CODE_ECHOREP: case IPPPP_CODE_DISCREQ: /* * These LCP codes are OK in a ML packet, * and should be handled by this netif, * so just return. */ /* Process with the same netif it came from */ return (ipppp_pppmp_input_pkt(netif, new_pkt)); default: /* * The rest should not be in a ML packet, * this packet should be discarded. */ goto discard; } break; case IPPPP_PROTOCOL_IPV4: case IPPPP_PROTOCOL_IPV6: /* * These are the packets I am looking for. * Send to the bundle netif, if necessary. */ { if (IP_BIT_ISSET(link->ppp_flags, IP_PPP_FLAG_MP_MEMBER)) { Ipcom_netif *bundle_netif = link->bundle_netif; int bundle_link_index = link->bundle_link_index; bundle_netif->link_index = bundle_link_index; new_pkt->ifindex = bundle_netif->ifindex; return (ipppp_pppmp_input_pkt(bundle_netif, new_pkt)); } } return (ipppp_pppmp_input_pkt(netif, new_pkt)); default: ipppp_log(IPCOM_LOG_DEBUG, netif, IP_NULL, "ipppp_multilink_input: multilink pkt but unknown protocol type: 0x%04x", eprotocol); goto discard; } discard: IPPPP_STATS(input_discard++); ipcom_pkt_free(pkt); return 0; } /* *=========================================================================== * ipppp_pppmp_output *=========================================================================== * Description: Queue * Parameters: * Returns: * */ IP_GLOBAL int ipppp_pppmp_output(Ipcom_netif *netif, Ipcom_pkt *pkt, Ipppp_link *link, Ip_u16 protocol) { Ipppp_netif *pppif = netif->plink; Ipppp_link *output_link; Ipcom_pkt *new_pkt; Ip_size_t new_pktsize; int i, j; int ret; int minsize, fragsize; int numfrags; int pktsize; int remaining; int start; int ssn = link->drv.ssn; int frag_disable = pppif->data.mp.frag_disable; IPCOM_UNUSED_ARG(link); /* No encapsulation for LCP */ if (((protocol != IPPPP_PROTOCOL_IPV4) && (protocol != IPPPP_PROTOCOL_IPV6)) || (IP_BIT_ISFALSE(pppif->pppflags, IPPPP_PPPIFFLAG_MP_BUNDLE)) || (pppif->data.mp.open_links < 1)) { ipppp_log(IPCOM_LOG_DEBUG, netif, IP_NULL, "discarding output - wrong protocol or unable to send multilink packet."); ipcom_pkt_free(pkt); return (0); } ip_assert (pppif->data.mp.open_links > 0); /* linearizing the packet */ pkt = ipcom_pkt_make_linear(pkt); if (pkt == IP_NULL) { IPPPP_STATS(input_err_esc_enomem++); ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, "discarding packet - out of memory"); /* the original packet has already beem freed in ipcom_pkt_make_linear() */ return (0); } pktsize = pkt->end - pkt->start; /* find the minimum MRU among all the currently open links */ minsize = pppif->data.mp.min_mtu; ip_assert(minsize > 0); if ((frag_disable == 0) || (pktsize > minsize)) /* If fragmentation allowed or pktsize bigger than one of the links MTU then fragment the packet */ { /* If you divide the packet among the open links, * will all fragments be smaller * than the smallest MRU? */ fragsize = pktsize / pppif->data.mp.open_links; if (fragsize > minsize) fragsize = minsize; else if (fragsize < (IPPPP_HDR_SIZE + (ssn ? 4 : 6))) fragsize = (IPPPP_HDR_SIZE + (ssn ? 4 : 6)) * 10; /* Arbitrary, but enough to avoid silly pkt size */ numfrags = pktsize / fragsize + 1; /* Set a limit on number of loop iterations */ remaining = pktsize; } else { fragsize = pktsize; numfrags = 1; remaining = pktsize; } if (pktsize > pppif->data.mp.peer_mrru) { ipppp_log(IPCOM_LOG_DEBUG, netif, IP_NULL, "discarding output - packet exceeds peer MRRU."); ipcom_pkt_free(pkt); return (0); } /* Adjust for going from two-byte to one-byte protocol */ start = pkt->start+1; remaining--; for (i = 0; remaining > 0 && i < numfrags; i++) { Ip_u16 workVar = 0; Ip_u16 thisfrag_flags = 0; int thisfrag_size = 0; if (remaining == fragsize+1) { /* Special case, allow because of rounding error */ thisfrag_size = fragsize + 1; remaining = 0; } else if (remaining > fragsize) { thisfrag_size = fragsize; remaining -= fragsize; } else { thisfrag_size = remaining; remaining = 0; } if ((frag_disable == 0) || (pktsize > minsize)) /* If fragmentation allowed or pktsize bigger than one of the links MTU then fragment the packet */ { if (i == 0) IP_BIT_SET(thisfrag_flags, IPPPP_MPHEADER_FIRST_FRAGMENT); if (remaining == 0) IP_BIT_SET(thisfrag_flags, IPPPP_MPHEADER_LAST_FRAGMENT); } else { IP_BIT_SET(thisfrag_flags, IPPPP_MPHEADER_ONLY_FRAGMENT); } /* Advance to the next active link */ for (j = 0; j < IPPPP_MAX_LINKS; j++) { int try_index = (pppif->data.mp.current_link+j+1) % IPPPP_MAX_LINKS; Ipppp_link * tlink = &pppif->link[try_index]; if ((IP_BIT_ISSET(tlink->data.flags, IPPPP_LINKFLAG_UP)) && (tlink->phases[IPPPP_PHASE_LCP].state == IPPPP_STATE_OPENED)) { pppif->data.mp.current_link = try_index; break; } } output_link = &pppif->link[pppif->data.mp.current_link]; netif->link_index = output_link->link_index; /* Create a new packet with additional bytes for the MP header */ new_pktsize = output_link->drv.mru + IPPPP_HDR_SIZE; new_pktsize = IP_MIN((Ip_size_t) (thisfrag_size + (ssn ? 4 : 6)), new_pktsize); new_pktsize = IP_MAX(IPPPP_PKTSIZE, new_pktsize); new_pkt = ipcom_pkt_malloc(new_pktsize, IP_FLAG_FC_STACKCONTEXT); if (new_pkt == IP_NULL) { IPPPP_STATS(input_err_esc_enomem++); ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, "discarding output packet - out of memory for new packet"); ipcom_pkt_free(pkt); return (0); } new_pkt->fd = pkt->fd; new_pkt->ifindex = pkt->ifindex; new_pkt->link_cookie = output_link->link_index; new_pkt->start = ipcom_conf_max_link_hdr_size; IP_SET_HTONS(&new_pkt->data[new_pkt->start], IPPPP_PROTOCOL_MP); if (ssn) { workVar = (Ip_u16)(((pppif->data.mp.send_seqnum) & 0x00000FFF) | (thisfrag_flags & 0xC000)); IP_SET_HTONS(&new_pkt->data[new_pkt->start + 2], workVar ); pppif->data.mp.send_seqnum = (pppif->data.mp.send_seqnum+1) & 0x00000FFF; } else { workVar = (Ip_u16)((((pppif->data.mp.send_seqnum) & 0x00FF0000) >> 16) | (thisfrag_flags & 0xC000)); IP_SET_HTONS(&new_pkt->data[new_pkt->start + 2], workVar ); workVar = (Ip_u16)((pppif->data.mp.send_seqnum) & 0x0000FFFF); IP_SET_HTONS(&new_pkt->data[new_pkt->start + 4], workVar ); pppif->data.mp.send_seqnum = (pppif->data.mp.send_seqnum+1) & 0x00FFFFFF; } ipcom_memcpy(&new_pkt->data[new_pkt->start + (ssn ? 4 : 6)], &pkt->data[start], thisfrag_size); new_pkt->end = new_pkt->start + thisfrag_size + (ssn ? 4 : 6); start+= thisfrag_size; /* Handle redirection if this goes to another netif */ if (IP_BIT_ISSET(output_link->ppp_flags, IP_PPP_FLAG_MP_SHADOW)) { Ipcom_netif * member_netif = output_link->member_netif; int member_link_index = output_link->member_link_index; Ipppp_netif *member_pppif = member_netif->plink; Ipppp_link *member_link = &member_pppif->link[member_link_index]; ret = ipppp_pppmp_output_fragment(member_netif, new_pkt, member_link, protocol); } else ret = ipppp_pppmp_output_fragment(netif, new_pkt, output_link, protocol); ip_assert(ret <= 0); if (ret < 0) { IPPPP_STATS(outputd_discard_drvfail++); ipppp_log(IPCOM_LOG_WARNING, netif, IP_NULL, "discarding output packet - driver failure : %s", ipcom_strerror(-ret)); return ret; } } /* all done, free the original packet */ ipcom_pkt_free ((Ipcom_pkt *) pkt); return (0); } #else int ipppp_pppmp_empty_file; #endif /* IPPPP_USE_PPPMP */ /* **************************************************************************** * END OF FILE **************************************************************************** */