2081 lines
68 KiB
C
2081 lines
68 KiB
C
/*
|
|
******************************************************************************
|
|
* 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 <Tim.Neale@windriver.com>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 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 <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_clib2.h>
|
|
#include <ipcom_auth.h>
|
|
#include <ipcom_netif.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_sysvar.h>
|
|
|
|
#include "ipppp_config.h"
|
|
#include "ipppp_h.h"
|
|
|
|
#ifdef IPPPP_USE_PPPMP
|
|
#ifdef IPCOM_USE_AUTH_RADIUS
|
|
#include <ipcom_tags.h>
|
|
#include <ipradius.h>
|
|
#endif
|
|
#ifdef IPDHCPS6
|
|
#include <ipdhcps6.h>
|
|
#endif
|
|
|
|
#ifdef IPNET
|
|
#include <ipnet_h.h>
|
|
#elif defined(IPLITE)
|
|
#include <iplite_h.h>
|
|
#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
|
|
****************************************************************************
|
|
*/
|