1844 lines
62 KiB
C
1844 lines
62 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_RCPL8_2024030303 $ $RCSfile: ipppp_pppoe.c,v $ $Revision: 1.59.10.10 $
|
|
* $Source: /home/interpeak/CVSRoot/ipppp/src/ipppp_pppoe.c,v $
|
|
* $Author: csun $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Lennart Bang <lob@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
* PPPoE implementation for IPPPP based on the following RFC:
|
|
* ==============================================================
|
|
* RFC 2516 - A Method for Transmitting PPP Over Ethernet (PPPoE)
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifndef IPCOM_KERNEL
|
|
#define IPCOM_KERNEL
|
|
#endif
|
|
#include "ipppp_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IPPPP_USE_PPPOE
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_syslog.h>
|
|
|
|
#include "ipppp_h.h"
|
|
|
|
#ifdef IPNET
|
|
#include <ipnet_h.h>
|
|
#include <ipnet_eth.h>
|
|
#elif defined(IPLITE)
|
|
#include <iplite_h.h>
|
|
#else
|
|
#error MUST_CHOOSE_STACK
|
|
#endif
|
|
|
|
#ifdef OPENSSL_FIPS
|
|
#include <openssl/fips.h>
|
|
#endif
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPPPP_PPPOE_LINK_INDEX 0 /* no reason to run MPPP on PPPoE */
|
|
#define PPPOE_TYPE_CLIENT 0
|
|
#define PPPOE_TYPE_SERVER 1
|
|
#define PPPOE_SESSIONID_WRAP_THRESHOLD 0x8000
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ethernet stuff
|
|
*===========================================================================
|
|
*/
|
|
|
|
#define IPPPP_PPPOE_MAC_DIS 0x8863
|
|
#define IPPPP_PPPOE_MAC_SES 0x8864
|
|
#define IPPPP_ETH_ADDR_SIZE 6
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* PPPoE tags
|
|
*===========================================================================
|
|
*/
|
|
#define IPPPP_PPPOE_TAG_HSIZE 4
|
|
#define IPPPP_PPPOE_NON_ALGORITHM_LENGTH 4
|
|
|
|
#define IPPPP_PPPOE_TAG_SET16(xpay, xlen, val16) \
|
|
do { \
|
|
xpay[xlen] = (Ip_u8)((val16 & 0xff00) >> 8); \
|
|
xlen++; \
|
|
xpay[xlen] = (Ip_u8)((val16 & 0x00ff)); \
|
|
xlen++; \
|
|
} while(0)
|
|
|
|
#define IPPPP_PPPOE_TAG_End_Of_List 0x0000
|
|
#define IPPPP_PPPOE_TAG_Service_Name 0x0101
|
|
#define IPPPP_PPPOE_TAG_AC_Name 0x0102
|
|
#define IPPPP_PPPOE_TAG_Host_Uniq 0x0103
|
|
#define IPPPP_PPPOE_TAG_AC_Cookie 0x0104
|
|
#define IPPPP_PPPOE_TAG_Vendor_Specific 0x0105
|
|
#define IPPPP_PPPOE_TAG_Relay_Session_Id 0x0110
|
|
#define IPPPP_PPPOE_TAG_Service_Name_Error 0x0201
|
|
#define IPPPP_PPPOE_TAG_AC_System_Error 0x0202
|
|
#define IPPPP_PPPOE_TAG_Generic_Error 0x0203
|
|
|
|
#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
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
typedef struct Ipppp_pkt_pppoe_struct
|
|
{
|
|
Ip_u8 ver_type; /* Version and code, always 0x11 */
|
|
Ip_u8 code;
|
|
Ip_u16 session_id;
|
|
Ip_u16 length;
|
|
Ip_u8 payload[1];
|
|
}
|
|
Ipppp_pkt_pppoe;
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipppp_pkt_eth
|
|
*===========================================================================
|
|
* PPPoeE header from RFC 2516.
|
|
*/
|
|
|
|
typedef struct Ipppp_pkt_eth_struct
|
|
{
|
|
Ip_u8 eth_dst[6]; /* Destination Ethernet address. */
|
|
Ip_u8 eth_src[6]; /* Sender Ethernet address. */
|
|
Ip_u16 eth_type; /* Ethernet type, e.g. IP = 0x0800 etc. */
|
|
}
|
|
Ipppp_pkt_eth;
|
|
|
|
|
|
/* PPPoE ver_type value. */
|
|
#define IPPPP_PPPOE_HDR_VERTYPE 0x11
|
|
|
|
/* PPPoE code */
|
|
#define IPPPP_PPPOE_HDR_CODE_PADI 0x9
|
|
#define IPPPP_PPPOE_HDR_CODE_PADO 0x7
|
|
#define IPPPP_PPPOE_HDR_CODE_PADR 0x19
|
|
#define IPPPP_PPPOE_HDR_CODE_PADS 0x65
|
|
#define IPPPP_PPPOE_HDR_CODE_PADT 0xa7
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_EXTERN Ipppp_data ipppp;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_STATIC void ipppp_pppoe_if_detach(Ipcom_netif *pppif);
|
|
IP_STATIC Ipcom_netif *ipppp_pppoe_if_malloc(char *ifname, int iftype);
|
|
|
|
IP_STATIC void ipppp_pppoe_timeout(Ipcom_netif *ethif);
|
|
IP_STATIC void ipppp_pppoe_send_padi(Ipcom_netif *ethif);
|
|
|
|
IP_GLOBAL int ipppp_pppoe_input(Ipcom_netif *ethif, Ipcom_pkt *pkt);
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
* Ethernet broadcast MAC
|
|
*/
|
|
const Ip_u16 ipppp_eth_addr_broadcast_storage[] = {
|
|
0xffff, 0xffff, 0xffff
|
|
};
|
|
const Ip_u8 *ipppp_eth_addr_broadcast = (Ip_u8 *) ipppp_eth_addr_broadcast_storage;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_set_cookie
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
#ifdef IPPPP_PPPOE_USE_MD5
|
|
IP_STATIC void
|
|
ipppp_pppoe_set_cookie(Ip_u8 *buf, Ip_u8 *eth_src)
|
|
{
|
|
MD5_CTX ctx;
|
|
char *name;
|
|
Ip_size_t namelen;
|
|
|
|
#ifdef OPENSSL_FIPS
|
|
if (FIPS_mode())
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING,IP_NULL, IP_NULL, "<ippppoe> :: ipppp_pppoe_set_cookie with MD5 as OPENSSL FIPS is not enabled");
|
|
return;
|
|
}
|
|
#endif
|
|
MD5_Init(&ctx);
|
|
MD5_Update(&ctx, eth_src, 6);
|
|
MD5_Update(&ctx, ipppp.pppoe.secret, IPPPP_PPPOE_SECRET_SIZE);
|
|
|
|
namelen = sizeof(ipppp.buf);
|
|
name = ipcom_sysvar_get("ipppp.pppoe.secret_name", ipppp.buf, &namelen);
|
|
if (name)
|
|
{
|
|
namelen = ipcom_strlen(name);
|
|
MD5_Update(&ctx, name, namelen);
|
|
}
|
|
MD5_Final(buf, &ctx);
|
|
}
|
|
#endif /* IPPPP_PPPOE_USE_MD5 */
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_find_netif
|
|
*===========================================================================
|
|
* Description: Given the ifindex of an ethernet I/F,
|
|
* check all the pppoe interfaces that are attached to it
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipcom_netif *
|
|
ipppp_pppoe_find_netif(Ip_u32 ethindex)
|
|
{
|
|
Ipcom_netif * netif = IP_NULL;
|
|
Ipppp_netif * pppif;
|
|
Ip_u32 i;
|
|
#ifdef IPPPP_USE_PPPMP
|
|
Ip_u32 j;
|
|
#endif
|
|
|
|
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_PPPOE) &&
|
|
((pppif = (Ipppp_netif *) netif->plink) != IP_NULL) &&
|
|
(IP_BIT_ISFALSE(netif->flags, IP_IFF_RUNNING)) &&
|
|
(pppif->pppoe_eth_ifindex == ethindex)
|
|
)
|
|
{
|
|
Ipppp_link * link;
|
|
|
|
#ifdef IPPPP_USE_PPPMP
|
|
for (j = 0; j < IPPPP_MAX_LINKS; j++)
|
|
{
|
|
link = &pppif->link[j];
|
|
|
|
if (link->phases[IPPPP_PHASE_LCP].state == IPPPP_STATE_STARTING)
|
|
return (netif);
|
|
}
|
|
#else
|
|
link = &pppif->link[0];
|
|
if (link->phases[IPPPP_PHASE_LCP].state == IPPPP_STATE_STARTING)
|
|
return (netif);
|
|
#endif /* IPPPP_USE_PPPMP */
|
|
|
|
}
|
|
}
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_sessionidtonetif
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipcom_netif *
|
|
ipppp_pppoe_sessionidtonetif(Ip_u32 session_id)
|
|
{
|
|
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_PPPOE) &&
|
|
((pppif = (Ipppp_netif *) netif->plink) != IP_NULL) &&
|
|
(IP_BIT_ISSET(netif->flags, IP_IFF_RUNNING))
|
|
)
|
|
{
|
|
#ifdef IPPPP_USE_PPPMP
|
|
Ipppp_link * link;
|
|
int j;
|
|
|
|
for (j = 0; j < IPPPP_MAX_LINKS; j++)
|
|
{
|
|
link = &pppif->link[j];
|
|
|
|
if ((IP_BIT_ISSET(link->data.flags, IPPPP_LINKFLAG_UP)) &&
|
|
(link->data.pppoe_session_id == session_id))
|
|
{
|
|
netif->link_index = link->link_index;
|
|
return (netif);
|
|
}
|
|
}
|
|
#else
|
|
if (session_id == pppif->pppoe_peer_session_id_n)
|
|
return (netif);
|
|
#endif /* IPPPP_USE_PPPMP */
|
|
}
|
|
}
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_ascii_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC char *
|
|
ipppp_pppoe_ascii_addr(Ip_u8 *num_addr)
|
|
{
|
|
int i;
|
|
static Ip_u8 ascii_addr[24];
|
|
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
ipcom_sprintf((char*)ascii_addr + (i*3), "%02x", (int)num_addr[i]);
|
|
if (i < 5)
|
|
ascii_addr[2 + (i*3)] = ':';
|
|
}
|
|
return (char *)ascii_addr;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_padx_print
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
#ifdef IP_DEBUG
|
|
IP_STATIC void
|
|
ipppp_pppoe_padx_print(Ipcom_netif *netif, Ipppp_pkt_pppoe *pppoe_hdr, Ip_bool input, Ip_s32 ethindex)
|
|
{
|
|
ipppp_log(IPCOM_LOG_DEBUG, netif, IP_NULL, "<ippppoe> :: %s %s [len %d] [ses %d] [eth %ld]",
|
|
input ? "receiving" : "sending",
|
|
pppoe_hdr->code == IPPPP_PPPOE_HDR_CODE_PADI ? "PADI" :
|
|
pppoe_hdr->code == IPPPP_PPPOE_HDR_CODE_PADO ? "PADO" :
|
|
pppoe_hdr->code == IPPPP_PPPOE_HDR_CODE_PADR ? "PADR" :
|
|
pppoe_hdr->code == IPPPP_PPPOE_HDR_CODE_PADS ? "PADS" :
|
|
pppoe_hdr->code == IPPPP_PPPOE_HDR_CODE_PADT ? "PADT" : "unknown",
|
|
ip_ntohs(pppoe_hdr->length),
|
|
ip_ntohs(pppoe_hdr->session_id),
|
|
ethindex);
|
|
}
|
|
#else
|
|
#define ipppp_pppoe_padx_print(x,y,z,w) IP_NOOP
|
|
#endif
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_timeout
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipppp_pppoe_timeout(Ipcom_netif *ethif)
|
|
{
|
|
ipppp_pppoe_send_padi(ethif);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_send_padt
|
|
*===========================================================================
|
|
* Description: Send Terminate packet
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipppp_pppoe_send_padt(Ipcom_netif *ethif, Ipppp_pkt_eth *in, Ip_u16 session_id_n)
|
|
{
|
|
Ipcom_pkt *pkt;
|
|
Ipppp_pkt_pppoe *pppoe_hdr;
|
|
|
|
/* Get memory for the PADT packet. */
|
|
pkt = ipcom_pkt_malloc(sizeof(Ipppp_pkt_eth) + 256, IP_FLAG_FC_STACKCONTEXT);
|
|
if (pkt == IP_NULL)
|
|
return;
|
|
|
|
pkt->ifindex = ethif->ifindex;
|
|
pkt->fd = -1;
|
|
ipcom_pkt_reserve_data(pkt, 0);
|
|
|
|
/* PPPoE header. */
|
|
pppoe_hdr = ipcom_pkt_push_front(pkt, IPPPP_PPPOE_HDR_SIZE);
|
|
pppoe_hdr->ver_type = IPPPP_PPPOE_HDR_VERTYPE;
|
|
pppoe_hdr->code = IPPPP_PPPOE_HDR_CODE_PADT;
|
|
pppoe_hdr->session_id = session_id_n;
|
|
pppoe_hdr->length = 0;
|
|
|
|
/* Ethernet header. */
|
|
ipnet_eth_add_hdr(pkt,
|
|
(void *) ethif,
|
|
in->eth_src,
|
|
ethif->link_addr,
|
|
ip_htons(IPPPP_PPPOE_MAC_DIS));
|
|
|
|
/* Send the PADT. */
|
|
ipppp_pppoe_padx_print(ethif, pppoe_hdr, IP_FALSE, (Ip_s32)ethif->ifindex);
|
|
(void)ipcom_ppp_ioevent(ethif, IPPPP_EIOXOUTPUT_IF, pkt);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_send_padi
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipppp_pppoe_send_padi(Ipcom_netif *ethif)
|
|
{
|
|
Ipcom_pkt *pkt;
|
|
Ipppp_pkt_pppoe *pppoe_hdr;
|
|
char *service_name;
|
|
Ip_size_t service_name_len;
|
|
Ip_u16 tag_length;
|
|
Ipnet_netif *netif = (Ipnet_netif *)ethif;
|
|
int vr = ipcom_proc_vr_get();
|
|
|
|
/* Get memory for the PADI packet. */
|
|
pkt = ipcom_pkt_malloc(sizeof(Ipppp_pkt_eth) + 256, IP_FLAG_FC_STACKCONTEXT);
|
|
if (pkt == IP_NULL)
|
|
return;
|
|
|
|
/* Set virtual router index */
|
|
if (vr != netif->vr_index)
|
|
ipcom_proc_vr_set(netif->vr_index);
|
|
|
|
pkt->ifindex = ethif->ifindex;
|
|
pkt->fd = -1;
|
|
pkt->start = ipcom_conf_max_link_hdr_size;
|
|
pkt->end = pkt->start;
|
|
pkt->ipstart = pkt->start;
|
|
|
|
/* PPPoE header */
|
|
pppoe_hdr = ipcom_pkt_push_back(pkt, IPPPP_PPPOE_HDR_SIZE);
|
|
pppoe_hdr->ver_type = IPPPP_PPPOE_HDR_VERTYPE;
|
|
pppoe_hdr->code = IPPPP_PPPOE_HDR_CODE_PADI;
|
|
pppoe_hdr->session_id = 0;
|
|
|
|
/* PPPoE tags */
|
|
tag_length = 0;
|
|
|
|
/* Add Service_Name tag for PADO packet reply */
|
|
IPPPP_PPPOE_TAG_SET16(pppoe_hdr->payload, tag_length, IPPPP_PPPOE_TAG_Service_Name);
|
|
service_name_len = sizeof(ipppp.buf);
|
|
service_name = ipcom_sysvar_get("ipppp.pppoe.service_name", ipppp.buf, &service_name_len);
|
|
if (service_name == IP_NULL || service_name_len < 1)
|
|
{
|
|
IPPPP_PPPOE_TAG_SET16(pppoe_hdr->payload, tag_length, 0);
|
|
}
|
|
else
|
|
{
|
|
IPPPP_PPPOE_TAG_SET16(pppoe_hdr->payload, tag_length, service_name_len);
|
|
ipcom_strcpy((char *)pppoe_hdr->payload + tag_length, ipppp.buf);
|
|
tag_length = (Ip_u16)(tag_length + service_name_len);
|
|
}
|
|
|
|
/* Ethernet header */
|
|
ipnet_eth_add_hdr(pkt,
|
|
(void *)ethif,
|
|
ipppp_eth_addr_broadcast,
|
|
ethif->link_addr,
|
|
ip_htons(IPPPP_PPPOE_MAC_DIS));
|
|
|
|
/* Move pkt end past the pppoe payload */
|
|
pkt->end += tag_length;
|
|
|
|
/* Send the PADI. */
|
|
pppoe_hdr->length = ip_htons(tag_length);
|
|
ipppp_pppoe_padx_print(ethif, pppoe_hdr, IP_FALSE, (Ip_s32)ethif->ifindex);
|
|
(void)ipcom_ppp_ioevent(ethif, IPPPP_EIOXOUTPUT_IF, pkt);
|
|
|
|
/* Restore virtual router index */
|
|
if (vr != netif->vr_index)
|
|
ipcom_proc_vr_set(vr);
|
|
|
|
/* Reset timer */
|
|
(void)IPSTACK_TIMEOUT_SCHEDULE(4000,
|
|
(Ipstack_timeout_handler)ipppp_pppoe_timeout,
|
|
ethif,
|
|
(Ipstack_timeout **)&((Ipstack_netif *)ethif)->eth->pppoe_tmo_handle);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_drv_ioctl
|
|
*===========================================================================
|
|
* Description: PPPoE driver ioctl. Always called code unlock.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipppp_pppoe_drv_ioctl(Ipcom_netif *netif, Ip_u32 command, void *data)
|
|
{
|
|
Ipppp_netif *pppif;
|
|
Ipppp_phase *phase;
|
|
Ipstack_netif *ethif = IP_NULL;
|
|
struct Ip_pppreq *pppreq = data;
|
|
int ret = 0;
|
|
unsigned long ifindexul;
|
|
unsigned long ioretul;
|
|
|
|
ip_assert(netif);
|
|
ip_assert(netif->type == IP_IFT_PPPOE);
|
|
ip_assert(netif->ifindex > 0);
|
|
|
|
pppif = netif->plink;
|
|
ip_assert(pppif);
|
|
|
|
#ifdef IPPPP_USE_PPPMP
|
|
phase = &pppif->link[netif->link_index].phases[IPPPP_PHASE_LINK];
|
|
#else
|
|
phase = &pppif->link[0].phases[IPPPP_PHASE_LINK];
|
|
#endif
|
|
|
|
switch (command)
|
|
{
|
|
case IP_SIOCXOPEN:
|
|
case IP_SIOCXCLOSE:
|
|
case IP_SIOCXDETACH:
|
|
case IP_SIOCIFDESTROY:
|
|
/* Lookup Ethernet interface */
|
|
ipppp_lock();
|
|
if (pppif->pppoe_eth_ifindex != 0)
|
|
{
|
|
ifindexul = (unsigned long)pppif->pppoe_eth_ifindex;
|
|
|
|
ioretul = (unsigned long)ipcom_ppp_ioevent(IP_NULL, IPPPP_EIOXINDEXTONETIF, (void *)ifindexul);
|
|
ethif = (Ipstack_netif *)ioretul;
|
|
|
|
if (ethif != IP_NULL && ethif->eth == IP_NULL)
|
|
ethif = IP_NULL; /* non-Ethernet interface, clear pointer */
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ip_assert(ethif == IP_NULL || ethif->eth != IP_NULL);
|
|
|
|
switch (command)
|
|
{
|
|
/***** Locked ioctls *****/
|
|
case IP_SIOCXOPEN:
|
|
/* Must be bound to Ethernet interface */
|
|
if (ethif == IP_NULL)
|
|
{
|
|
ret = -IP_ERRNO_ENXIO;
|
|
}
|
|
|
|
else if (IP_BIT_ISSET(netif->flags, IP_IFF_LINK1))
|
|
{
|
|
/* Client */
|
|
if (phase->state == IPPPP_STATE_INITIAL)
|
|
{
|
|
/* Initial state -> start searching */
|
|
ipppp_pppoe_send_padi((Ipcom_netif *)ethif);
|
|
phase->state = IPPPP_STATE_REQSENT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Server -> signal link up immediately. */
|
|
ipppp_unlock();
|
|
(void)netif->link_ioevent(netif, IP_EIOXUP, data, 0);
|
|
return 0;
|
|
}
|
|
goto unlock_leave;
|
|
|
|
case IP_SIOCXCLOSE:
|
|
if (IP_BIT_ISSET(netif->flags, IP_IFF_LINK1) && ethif != IP_NULL)
|
|
{
|
|
/* PPPoE client */
|
|
if (phase->state == IPPPP_STATE_REQSENT)
|
|
/* Cancel PADI timer */
|
|
IPSTACK_TIMEOUT_CANCEL(ethif->eth->pppoe_tmo_handle);
|
|
phase->state = IPPPP_STATE_INITIAL;
|
|
}
|
|
|
|
/* Signal link down immediately. */
|
|
(void)netif->link_ioevent(netif, IP_EIOXDOWN, data, IP_FLAG_FC_STACKCONTEXT);
|
|
goto unlock_leave;
|
|
|
|
case IP_SIOCXDETACH:
|
|
if (IP_BIT_ISSET(netif->flags, IP_IFF_LINK1) && ethif != IP_NULL)
|
|
{
|
|
/* PPPoE client */
|
|
if (phase->state == IPPPP_STATE_REQSENT)
|
|
{
|
|
/* Cancel PADI timer */
|
|
IPSTACK_TIMEOUT_CANCEL(ethif->eth->pppoe_tmo_handle);
|
|
}
|
|
phase->state = IPPPP_STATE_INITIAL;
|
|
}
|
|
goto unlock_leave;
|
|
|
|
case IP_SIOCIFDESTROY:
|
|
if (IP_BIT_ISSET(netif->flags, IP_IFF_RUNNING))
|
|
return -IP_ERRNO_EBUSY;
|
|
|
|
/* Detach & free interface */
|
|
ipppp_pppoe_if_detach(netif);
|
|
goto unlock_leave;
|
|
|
|
|
|
/***** Unlocked ioctls *****/
|
|
case IP_SIOCXPPPGDRVCONF:
|
|
pppreq->pppru.drvconf.drv_flags = IP_DRVPPP_FLAG_NOFLAG
|
|
| IP_DRVPPP_FLAG_NOESCAPE
|
|
| IP_DRVPPP_FLAG_NOFCS
|
|
| IP_DRVPPP_FLAG_NOACFC;
|
|
pppreq->pppru.drvconf.baudrate = 0;
|
|
pppreq->pppru.drvconf.rcv_accm = 0;
|
|
pppreq->pppru.drvconf.snd_accm = 0;
|
|
pppreq->pppru.drvconf.mru = 1492;
|
|
pppreq->pppru.drvconf.mtu = 1492;
|
|
break;
|
|
|
|
case IP_SIOCXPPPSETIF:
|
|
{
|
|
Ip_u32 ifindex;
|
|
|
|
/* check for run status on netif */
|
|
if (phase->state > IPPPP_STATE_INITIAL)
|
|
return -IP_ERRNO_EPERM;
|
|
|
|
#ifdef IPNET
|
|
ethif = (Ipstack_netif *)ipnet_if_nametonetif(IPCOM_VR_ANY, pppreq->pppru.if_name);
|
|
if (ethif == IP_NULL)
|
|
return -IP_ERRNO_ESRCH;
|
|
ifindex = ethif->ipcom.ifindex;
|
|
|
|
#else
|
|
ifindex = ipcom_if_nametoindex(pppreq->pppru.if_name);
|
|
if (ifindex == 0)
|
|
return -IP_ERRNO_ESRCH;
|
|
#endif /* IPNET */
|
|
|
|
ipppp_lock();
|
|
|
|
/* Get previous Ethernet interface (if any) */
|
|
ifindexul = (unsigned long)pppif->pppoe_eth_ifindex;
|
|
ioretul = (unsigned long)ipcom_ppp_ioevent(IP_NULL, IPPPP_EIOXINDEXTONETIF, (void *)ifindexul);
|
|
ethif = (Ipstack_netif *)ioretul;
|
|
|
|
if (ethif != IP_NULL && ethif->eth != IP_NULL)
|
|
{
|
|
/* Always unbind */
|
|
ethif->eth->pppoe_ppp_ifindex = 0;
|
|
pppif->pppoe_eth_ifindex = 0;
|
|
|
|
}
|
|
if (ifindex == netif->ifindex)
|
|
break; /* Bind to self, unbind */
|
|
|
|
/* Get new Ethernet interface */
|
|
ifindexul = (unsigned long)ifindex;
|
|
ioretul = (unsigned long)ipcom_ppp_ioevent(IP_NULL, IPPPP_EIOXINDEXTONETIF, (void *)ifindexul);
|
|
ethif = (Ipstack_netif *)ioretul;
|
|
|
|
if (ethif == IP_NULL || ethif->eth == IP_NULL)
|
|
{
|
|
ret = -IP_ERRNO_ENXIO;
|
|
}
|
|
else
|
|
{
|
|
/* Bind to new Ethernet interface */
|
|
pppif->pppoe_eth_ifindex = ethif->ipcom.ifindex;
|
|
ethif->eth->pppoe_ppp_ifindex = netif->ifindex;
|
|
}
|
|
goto unlock_leave;
|
|
}
|
|
/* never gets to this point, no break needed here */
|
|
|
|
default:
|
|
ret = -IP_ERRNO_EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
|
|
unlock_leave:
|
|
ipppp_unlock();
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_drv_output
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipppp_pppoe_drv_output(Ipcom_netif *netif, Ipcom_pkt *pkt)
|
|
{
|
|
Ipppp_netif *pppif;
|
|
Ipcom_netif *ethif;
|
|
Ipppp_pkt_pppoe *pppoe_hdr;
|
|
unsigned long ifindexul;
|
|
unsigned long ioretul;
|
|
int ret;
|
|
|
|
ip_assert(netif);
|
|
ip_assert(netif->type == IP_IFT_PPPOE);
|
|
ip_assert(netif->ifindex > 0);
|
|
|
|
pppif = netif->plink;
|
|
ip_assert(pppif);
|
|
|
|
/* Lookup Ethernet interface that is bound to the PPPoE interface */
|
|
ifindexul = (unsigned long)pppif->pppoe_eth_ifindex;
|
|
ioretul = (unsigned long)ipcom_ppp_ioevent(IP_NULL, IPPPP_EIOXINDEXTONETIF, (void *)ifindexul);
|
|
ethif = (Ipcom_netif *)ioretul;
|
|
if (ethif == IP_NULL)
|
|
{
|
|
ret = -IP_ERRNO_ENETDOWN;
|
|
goto discard;
|
|
}
|
|
|
|
if (!IP_BIT_ISSET(ethif->flags, IP_IFF_UP))
|
|
{
|
|
ret = -IP_ERRNO_ENETDOWN;
|
|
goto discard;
|
|
}
|
|
|
|
#ifdef IPNET_USE_VLAN
|
|
ip_assert(ethif->type == IP_IFT_ETHER || ethif->type == IP_IFT_L2VLAN);
|
|
#else
|
|
ip_assert(ethif->type == IP_IFT_ETHER);
|
|
#endif
|
|
|
|
(void)ipcom_ppp_ioevent(ethif, IPPPP_EIOXIF_LOCK, IP_NULL);
|
|
|
|
#ifdef IPPPP_USE_PPPMP
|
|
if (IP_BIT_ISSET(pppif->pppflags, IPPPP_PPPIFFLAG_MP_BUNDLE) ||
|
|
IP_BIT_ISSET(pppif->pppflags, IPPPP_PPPIFFLAG_MP_MEMBER))
|
|
{
|
|
Ipppp_link * link = &pppif->link[netif->link_index];
|
|
pppif->pppoe_peer_session_id_n = (Ip_u16)link->data.pppoe_session_id;
|
|
}
|
|
#endif /* IPPPP_USE_PPPMP */
|
|
|
|
/* PPPoE header. */
|
|
pppoe_hdr = ipcom_pkt_push_front(pkt, IPPPP_PPPOE_HDR_SIZE);
|
|
pppoe_hdr->ver_type = IPPPP_PPPOE_HDR_VERTYPE;
|
|
pppoe_hdr->code = 0;
|
|
pppoe_hdr->length = (Ip_u16)(ipcom_pkt_get_length(pkt) - IPPPP_PPPOE_HDR_SIZE);
|
|
pppoe_hdr->length = ip_htons(pppoe_hdr->length);
|
|
if (pppif->pppoe_peer_session_id_n != 0)
|
|
pppoe_hdr->session_id = (Ip_u16)ip_htons(pppif->pppoe_peer_session_id_n); /* client mode */
|
|
else
|
|
pppoe_hdr->session_id = (Ip_u16)ip_htons(netif->ifindex);
|
|
|
|
pkt->ifindex = ethif->ifindex;
|
|
|
|
/* Ethernet header. */
|
|
ipnet_eth_add_hdr(pkt,
|
|
(void *) ethif,
|
|
pppif->pppoe_peer_eth_addr,
|
|
ethif->link_addr,
|
|
ip_htons(IPPPP_PPPOE_MAC_SES));
|
|
|
|
/* Output the PPPoE packet on the Ethernet. */
|
|
ret = (int)ipcom_ppp_ioevent(ethif, IPPPP_EIOXOUTPUT_IF, pkt);
|
|
(void)ipcom_ppp_ioevent(ethif, IPPPP_EIOXIF_UNLOCK, IP_NULL);
|
|
|
|
/* Don't propagate the error code; we may do a double free in the non blocking case */
|
|
return 0;
|
|
|
|
discard:
|
|
ipcom_pkt_free(pkt);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_if_malloc
|
|
*===========================================================================
|
|
* Description: Create PPPoE interface. Must be called locked!
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipcom_netif *
|
|
ipppp_pppoe_if_malloc(char *ifname, int iftype)
|
|
{
|
|
Ipcom_netif *netif;
|
|
Ipppp_netif *pppif;
|
|
|
|
/* Allocate netif as being PPP, then change to PPPoE. */
|
|
netif = ipcom_if_malloc(IP_IFT_PPP);
|
|
if (netif == IP_NULL)
|
|
return IP_NULL;
|
|
|
|
/* Init PPPoE pppif stuff. */
|
|
netif->link_index = IPPPP_PPPOE_LINK_INDEX;
|
|
netif->type = IP_IFT_PPPOE;
|
|
netif->mtu = 1492;
|
|
netif->link_hdr_size = IPPPP_ETH_HDR_SIZE + IPPPP_PPPOE_HDR_SIZE + 2;
|
|
netif->drv_ioctl = ipppp_pppoe_drv_ioctl;
|
|
netif->drv_output = ipppp_pppoe_drv_output;
|
|
|
|
/* Interface name */
|
|
if (ifname)
|
|
{
|
|
ipcom_strncpy(netif->name, ifname, IP_IFNAMSIZ);
|
|
netif->name[IP_IFNAMSIZ-1] = 0;
|
|
}
|
|
else
|
|
{
|
|
int name_index;
|
|
name_index = ipcom_atoi(&netif->name[3]);
|
|
ipcom_sprintf(netif->name, "pppoe%d", name_index);
|
|
}
|
|
|
|
/* Large ifindex tells us that the session ID has (or is about to) wrap */
|
|
if ((iftype == PPPOE_TYPE_SERVER) &&
|
|
(ipnet->next_ifindex >= PPPOE_SESSIONID_WRAP_THRESHOLD))
|
|
{
|
|
Ip_u32 ifindex = ++ipnet->next_ifindex & 0x7fff;
|
|
|
|
/* Get an OK ifindex to use also for session ID */
|
|
while ((ifindex == 0) || (ipcom_hash_get(ipnet->netifs, &ifindex) != IP_NULL))
|
|
{
|
|
ifindex = (ifindex+1) & 0x7fff;
|
|
if (ifindex == (ipnet->next_ifindex & 0x7fff))
|
|
{
|
|
/* Tried all numbers, have to give up */
|
|
(void)ipcom_if_free(netif);
|
|
return IP_NULL;
|
|
}
|
|
}
|
|
netif->ifindex = ifindex;
|
|
}
|
|
|
|
/* Attach the PPPoE interface. */
|
|
if (ipcom_if_attach(netif) < 0)
|
|
{
|
|
(void)ipcom_if_free(netif);
|
|
return IP_NULL;
|
|
}
|
|
|
|
/* Configure PPPoE (must be locked) */
|
|
/*!MPPP RFC2516:7 Do not neg/ack ACFC (0xff 0x03) */
|
|
pppif = netif->plink;
|
|
pppif->link[0].ppp_flags |= (IP_PPP_FLAG_NOACCOMP | IP_PPP_FLAG_NOACCM);
|
|
return netif;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_if_create_server
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipcom_netif *
|
|
ipppp_pppoe_if_create_server(Ipcom_netif *ethif, Ipppp_pkt_eth *in_eth_hdr, Ipppp_pkt_pppoe *out_pppoe_hdr)
|
|
{
|
|
Ipcom_netif *netif;
|
|
Ipppp_netif *pppif;
|
|
|
|
#ifdef IPNET_USE_VLAN
|
|
ip_assert(ethif->type == IP_IFT_ETHER || ethif->type == IP_IFT_L2VLAN);
|
|
#else
|
|
ip_assert(ethif->type == IP_IFT_ETHER);
|
|
#endif
|
|
ip_assert(((Ipstack_netif *)ethif)->eth != IP_NULL);
|
|
|
|
/* Malloc PPPoE interface */
|
|
netif = ipppp_pppoe_if_malloc(IP_NULL, PPPOE_TYPE_SERVER);
|
|
if (netif == IP_NULL)
|
|
return IP_NULL;
|
|
|
|
/* Auto bind interface */
|
|
pppif = netif->plink;
|
|
pppif->pppoe_eth_ifindex = ethif->ifindex;
|
|
ipcom_memcpy(pppif->pppoe_peer_eth_addr, in_eth_hdr->eth_src, IPPPP_ETH_ADDR_SIZE);
|
|
|
|
/* Set out session ID. */
|
|
out_pppoe_hdr->session_id = (Ip_u16)ip_htons(netif->ifindex);
|
|
|
|
/* One more session. */
|
|
ipppp.pppoe.num_sessions++;
|
|
((Ipstack_netif *)ethif)->eth->pppoe_num_sessions++;
|
|
|
|
/* Success */
|
|
return netif;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_if_detach
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipppp_pppoe_if_detach(Ipcom_netif *netif)
|
|
{
|
|
Ipstack_netif *ethif;
|
|
Ipppp_netif *pppif;
|
|
int ret;
|
|
unsigned long ioretul;
|
|
unsigned long ifindexul;
|
|
|
|
ip_assert(netif->type == IP_IFT_PPPOE);
|
|
pppif = netif->plink;
|
|
ip_assert(pppif);
|
|
|
|
ipppp_log(IPCOM_LOG_INFO, netif, IP_NULL, "<ippppoe> :: detaching PPPoE [ses %ld]", netif->ifindex);
|
|
|
|
/* Reduce session count. */
|
|
ipppp.pppoe.num_sessions--;
|
|
ip_assert(ipppp.pppoe.num_sessions >= 0);
|
|
|
|
ifindexul = (unsigned long)pppif->pppoe_eth_ifindex;
|
|
ioretul = (unsigned long)ipcom_ppp_ioevent(IP_NULL, IPPPP_EIOXINDEXTONETIF, (void *)ifindexul);
|
|
ethif = (Ipstack_netif *)ioretul;
|
|
|
|
if (ethif != IP_NULL)
|
|
{
|
|
ip_assert(ethif->eth != IP_NULL);
|
|
ethif->eth->pppoe_num_sessions--;
|
|
ethif->eth->pppoe_ppp_ifindex = 0;
|
|
ip_assert(ethif->eth->pppoe_num_sessions >= 0);
|
|
}
|
|
|
|
/* Detach interface. */
|
|
ret = ipcom_if_detach(netif);
|
|
ip_assert(ret > 0);
|
|
if (ret > 0)
|
|
(void)ipcom_if_free(netif); /* Free interface. */
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_create
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipppp_pppoe_create(struct Ip_ifreq *ifreq)
|
|
{
|
|
Ipcom_netif *netif;
|
|
Ipppp_netif *pppif;
|
|
Ipppp_link *link;
|
|
Ip_u32 i;
|
|
int vr = ipcom_proc_vr_get();
|
|
|
|
/* Set virtual router index */
|
|
if (vr != ifreq->ip_ifr_vr)
|
|
ipcom_proc_vr_set(ifreq->ip_ifr_vr);
|
|
|
|
/* Malloc PPPoE client interface */
|
|
netif = ipppp_pppoe_if_malloc(ifreq->ifr_name, PPPOE_TYPE_CLIENT);
|
|
|
|
/* Restore virtual router index */
|
|
if (vr != ifreq->ip_ifr_vr)
|
|
ipcom_proc_vr_set(vr);
|
|
|
|
if (netif == IP_NULL)
|
|
return -IP_ERRNO_ENOMEM;
|
|
|
|
IP_BIT_SET(netif->flags, IP_IFF_LINK1); /* PPPoE client interface */
|
|
|
|
/* Init PPP links. */
|
|
pppif = (Ipppp_netif *)netif->plink;
|
|
for (i = 0; i < IPPPP_MAX_LINKS; i++)
|
|
{
|
|
if (pppif && (link = &pppif->link[i]))
|
|
IP_BIT_SET(link->ppp_flags, IP_PPP_FLAG_NOPCOMP);
|
|
}
|
|
|
|
/* link states = initial, reqsent (PADI), opened */
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef IPPPP_USE_PPPMP
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_find_link
|
|
*===========================================================================
|
|
* Description: Find link that matches the session ID.
|
|
* Setting it to netif_index is previous behavior (was it?).
|
|
* Parameters:
|
|
* Returns: Link index, if one is found, otherwise -1.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipppp_pppoe_find_link(Ipcom_netif *netif, int session_id)
|
|
{
|
|
Ipppp_netif *pppif = netif->plink;
|
|
Ipppp_link *link;
|
|
int i, link_index;
|
|
|
|
if (IP_BIT_ISFALSE(pppif->pppflags, IPPPP_PPPIFFLAG_MP_BUNDLE))
|
|
link_index = netif->link_index;
|
|
else
|
|
{
|
|
link_index = -1;
|
|
for (i = 0; i < pppif->data.mp.mp_links; i++)
|
|
{
|
|
link = &pppif->link[i];
|
|
if (session_id == (int)link->data.pppoe_session_id)
|
|
{
|
|
link_index = netif->link_index = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return (link_index);
|
|
}
|
|
#endif /* IPPPP_USE_PPPMP */
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipppp_pppoe_input
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipppp_pppoe_input(Ipcom_netif *ethif, Ipcom_pkt *pkt)
|
|
{
|
|
Ipppp_pkt_eth *eth_hdr;
|
|
Ipppp_pkt_pppoe *pppoe_hdr;
|
|
Ipcom_pkt *reply_pkt = IP_NULL;
|
|
Ipcom_netif *netif = IP_NULL;
|
|
Ipppp_netif *pppif = IP_NULL;
|
|
Ipstack_netif_eth *ethif_eth = ((Ipstack_netif *)ethif)->eth;
|
|
unsigned long ioretul;
|
|
unsigned long ifindexul;
|
|
unsigned long sessionidul;
|
|
int ret;
|
|
int max_sessions;
|
|
int session_id;
|
|
int err_status; /* 0 ok, -1 error, 1 start PPPoE */
|
|
#ifdef IPPPP_USE_PPPMP
|
|
Ipppp_link *link;
|
|
#endif /* IPPPP_USE_PPPMP */
|
|
Ip_u16 eth_type;
|
|
int vr = ipcom_proc_vr_get();
|
|
|
|
#ifdef IPNET_USE_VLAN
|
|
ip_assert(ethif->type == IP_IFT_ETHER || ethif->type == IP_IFT_L2VLAN);
|
|
#else
|
|
ip_assert(ethif->type == IP_IFT_ETHER);
|
|
#endif
|
|
ip_assert(ethif_eth != IP_NULL);
|
|
|
|
/* Set virtual router index */
|
|
if (vr != pkt->vr_index)
|
|
ipcom_proc_vr_set(pkt->vr_index);
|
|
|
|
eth_hdr = (Ipppp_pkt_eth *)&pkt->data[pkt->start];
|
|
pppoe_hdr = (Ipppp_pkt_pppoe *)&pkt->data[pkt->start + ethif->link_hdr_size];
|
|
eth_type = (Ip_u16)IP_GET_NTOHS(&pkt->data[pkt->start + ethif->link_hdr_size - 2]);
|
|
|
|
/* Don't read my own output Ethernet packets. */
|
|
if (ipcom_memcmp(eth_hdr->eth_src, ethif->link_addr, IPPPP_ETH_ADDR_SIZE) == 0)
|
|
goto leave;
|
|
|
|
/* PPPoE version and type check. */
|
|
if (pppoe_hdr->ver_type != IPPPP_PPPOE_HDR_VERTYPE)
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING, ethif, IP_NULL, "<ippppoe> :: invalid PPPoE version 0x%x or type 0x%x [eth %ld]",
|
|
pppoe_hdr->ver_type >> 4, pppoe_hdr->ver_type & 0xf, ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
|
|
/* PPPoE length check. */
|
|
if (ip_ntohs(pppoe_hdr->length) > (IP_ETH_MTU - IPPPP_PPPOE_HDR_SIZE))
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING, ethif, IP_NULL, "<ippppoe> :: invalid PPPoE length %d [eth %ld]",
|
|
ip_ntohs(pppoe_hdr->length), ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
|
|
session_id = (int)ip_ntohs(pppoe_hdr->session_id);
|
|
|
|
/***** PPPoE session frames. *****/
|
|
if (eth_type == IPPPP_PPPOE_MAC_SES)
|
|
{
|
|
sessionidul = (unsigned long)session_id;
|
|
ioretul = (unsigned long)ipcom_ppp_ioevent(IP_NULL, IPPPP_EIOXINDEXTONETIF, (void *)sessionidul);
|
|
netif = (Ipcom_netif *)ioretul;
|
|
|
|
if (netif == IP_NULL || netif->type != IP_IFT_PPPOE || IP_BIT_ISSET(netif->flags, IP_IFF_LINK1))
|
|
{
|
|
/* If the peer session id fails check if client interface */
|
|
netif = ipppp_pppoe_sessionidtonetif((Ip_u32)session_id);
|
|
if (netif == IP_NULL)
|
|
{
|
|
ipppp_log(IPCOM_LOG_NOTICE,
|
|
ethif, IP_NULL, "<ippppoe> :: received PPPoE session packet with unknown session-id(%d) code (%d) [eth %ld]",
|
|
session_id,
|
|
pppoe_hdr->code,
|
|
ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
if (netif->type != IP_IFT_PPPOE || IP_BIT_ISFALSE(netif->flags, IP_IFF_LINK1))
|
|
goto leave;
|
|
pppif = netif->plink;
|
|
if (pppif->link[0].phases[IPPPP_PHASE_LINK].state < IPPPP_STATE_OPENED)
|
|
goto leave; /* discard PPP frames if pppoe client is in searching mode */
|
|
}
|
|
ip_assert(netif->type == IP_IFT_PPPOE);
|
|
pppif = netif->plink;
|
|
|
|
/* Lock this PPP interface. */
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_LOCK, IP_NULL);
|
|
|
|
/* PPPoE code must be set to 0 in session stage.
|
|
* Syslog a warning but let it slide.
|
|
*/
|
|
if (pppoe_hdr->code != 0)
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING,
|
|
ethif, IP_NULL, "<ippppoe> :: PPPoE session packet with non-zero code (%d) [in %s] [ses %ld] [eth %ld]",
|
|
pppoe_hdr->code,
|
|
ipppp_pppoe_ascii_addr(eth_hdr->eth_src),
|
|
netif->ifindex, ethif->ifindex);
|
|
goto send_padt;
|
|
}
|
|
|
|
/* Check Ethernet source address (must match). */
|
|
if (ipcom_memcmp(eth_hdr->eth_src, pppif->pppoe_peer_eth_addr, 6) != 0)
|
|
{
|
|
ipppp_log(IPCOM_LOG_DEBUG,
|
|
ethif, IP_NULL, "<ippppoe> :: session - Ethernet source address mismatch [in %s] [ses %ld] [eth %ld]",
|
|
ipppp_pppoe_ascii_addr(eth_hdr->eth_src), netif->ifindex, ethif->ifindex);
|
|
goto send_padt;
|
|
}
|
|
|
|
/* Success, pass PPP packet to ipppp_input(). */
|
|
pkt->start += ethif->link_hdr_size + IPPPP_PPPOE_HDR_SIZE;
|
|
ipcom_pkt_trim_tail(pkt, ipcom_pkt_get_length(pkt) - (int)ip_htons(pppoe_hdr->length));
|
|
pkt->ifindex = netif->ifindex;
|
|
|
|
#ifdef IPPPP_USE_PPPMP
|
|
/*
|
|
* Server use netif ifindex as session number and always use link 0
|
|
*/
|
|
if (IP_BIT_ISFALSE(netif->flags, IP_IFF_LINK1))
|
|
pkt->link_cookie = IPPPP_PPPOE_LINK_INDEX;
|
|
else
|
|
{
|
|
int link_found = ipppp_pppoe_find_link(netif, session_id);
|
|
|
|
if (link_found < 0)
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING, ethif, IP_NULL,
|
|
"<ippppoe> :: PPPoE session %d does not match an open link.",
|
|
session_id);
|
|
pkt->link_cookie = 0;
|
|
goto send_padt;
|
|
}
|
|
else
|
|
pkt->link_cookie = (Ip_ptrdiff_t)link_found;
|
|
}
|
|
#else
|
|
pkt->link_cookie = IPPPP_PPPOE_LINK_INDEX;
|
|
#endif
|
|
ret = ipppp_input(netif, pkt);
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_UNLOCK, IP_NULL);
|
|
|
|
/* Restore virtual router index */
|
|
if (vr != pkt->vr_index)
|
|
ipcom_proc_vr_set(vr);
|
|
|
|
return ret;
|
|
|
|
/* Send a PADT. */
|
|
send_padt:
|
|
if (netif != IP_NULL)
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_UNLOCK, IP_NULL);
|
|
ipppp_pppoe_send_padt(ethif, eth_hdr, pppoe_hdr->session_id);
|
|
goto leave;
|
|
}
|
|
|
|
|
|
/***** PPPoE discovery frames only. *****/
|
|
if (eth_type != IPPPP_PPPOE_MAC_DIS)
|
|
{
|
|
ipppp_log(IPCOM_LOG_ERR,
|
|
ethif, IP_NULL, "<ippppoe> :: invalid Ethernet frame 0x%x [eth %ld]",
|
|
eth_type, ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
|
|
/* for ppope control packet */
|
|
if (pkt->next_part != IP_NULL)
|
|
{
|
|
int vr_pkt = pkt->vr_index;
|
|
/* linearize pppoe control packet, that will not consume cpu too much. */
|
|
pkt = ipcom_pkt_make_linear(pkt);
|
|
if(pkt == IP_NULL)
|
|
{
|
|
/* Restore virtual router index */
|
|
if (vr != vr_pkt)
|
|
ipcom_proc_vr_set(vr);
|
|
|
|
return 0;
|
|
}
|
|
eth_hdr = (Ipppp_pkt_eth *)&pkt->data[pkt->start];
|
|
pppoe_hdr = (Ipppp_pkt_pppoe *)&pkt->data[pkt->start + ethif->link_hdr_size];
|
|
}
|
|
|
|
ipppp_pppoe_padx_print(ethif, pppoe_hdr, IP_TRUE, (Ip_s32)ethif->ifindex);
|
|
|
|
|
|
/***** Act on the PPPoE Discovery code *****/
|
|
switch(pppoe_hdr->code)
|
|
{
|
|
case IPPPP_PPPOE_HDR_CODE_PADI: /* PPPoE Active Discovery Initiation packet */
|
|
case IPPPP_PPPOE_HDR_CODE_PADR: /* PPPoE Active Discovery Request packet */
|
|
/***** Check if PPPoE server is enabled *****/
|
|
if (ipcom_sysvar_get_conf_as_int("ipppp.pppoe.server", IP_NULL, ethif->name) != 1)
|
|
{
|
|
/* Silent discard */
|
|
goto leave;
|
|
}
|
|
|
|
/* Ignore PADI or PADR if total number of sessions reached. */
|
|
max_sessions = ipcom_sysvar_get_as_int("ipppp.pppoe.max_sessions", 16);
|
|
if (ipppp.pppoe.num_sessions >= max_sessions)
|
|
{
|
|
ipppp_log(IPCOM_LOG_NOTICE,
|
|
ethif, IP_NULL, "<ippppoe> :: ignoring PPPoE PAD[I|R] - total sessions reached [%d/%d] [eth %ld]",
|
|
ipppp.pppoe.num_sessions, max_sessions, ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
|
|
/* Ignore PADI or PADR if max number of sessions per interface reached. */
|
|
max_sessions = ipcom_sysvar_get_conf_as_int("ipppp.pppoe.max_eth_sessions", IP_NULL, ethif->name);
|
|
if (ethif_eth->pppoe_num_sessions >= max_sessions)
|
|
{
|
|
ipppp_log(IPCOM_LOG_NOTICE,
|
|
ethif, IP_NULL, "<ippppoe> :: ignoring PPPoE PAD[I|R] - maximum interface sessions reached [%d/%d] [eth %ld]",
|
|
ethif_eth->pppoe_num_sessions, max_sessions, ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
/* fall through */
|
|
|
|
case IPPPP_PPPOE_HDR_CODE_PADO: /* PPPoE Active Discovery Offer packet */
|
|
/* Session_id must be 0 for all PAD[I|R|O] packets */
|
|
if (pppoe_hdr->session_id != 0)
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING, ethif, IP_NULL, "<ippppoe> :: invalid PPPoE PADx session_id 0x%x (must be 0) [eth %ld]",
|
|
session_id, ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
/* fall through */
|
|
|
|
case IPPPP_PPPOE_HDR_CODE_PADS: /* PPPoE Active Discovery Session-confirmation packet */
|
|
{
|
|
Ipppp_pkt_pppoe *reply_pppoe_hdr;
|
|
Ip_u8 *tagp;
|
|
Ip_u16 tag_type, tag_length, out_length;
|
|
int in_length;
|
|
Ip_bool service_ok = IP_FALSE;
|
|
#ifdef IPPPP_PPPOE_USE_MD5
|
|
#ifdef OPENSSL_FIPS
|
|
Ip_bool cookie_ok;
|
|
if (FIPS_mode())
|
|
cookie_ok = IP_TRUE;
|
|
else
|
|
cookie_ok = IP_FALSE;
|
|
#else
|
|
Ip_bool cookie_ok = IP_FALSE;
|
|
#endif /* end OPENSSL_FIPS */
|
|
#else
|
|
Ip_bool cookie_ok = IP_TRUE;
|
|
#endif
|
|
|
|
/* Get memory for the PAD[O|S|R] reply. */
|
|
reply_pkt = ipcom_pkt_malloc(sizeof(Ipppp_pkt_eth) + 512, IP_FLAG_FC_STACKCONTEXT);
|
|
if (reply_pkt == IP_NULL)
|
|
{
|
|
ipppp_log(IPCOM_LOG_ERR, ethif, IP_NULL, "<ippppoe> :: ipcom_pkt_malloc() failed, no memory");
|
|
goto leave;
|
|
}
|
|
reply_pkt->ifindex = ethif->ifindex;
|
|
reply_pkt->fd = -1;
|
|
reply_pkt->start = ipcom_conf_max_link_hdr_size;
|
|
reply_pkt->ipstart = reply_pkt->start;
|
|
reply_pkt->end = reply_pkt->start;
|
|
|
|
reply_pppoe_hdr = ipcom_pkt_push_back(reply_pkt, IPPPP_PPPOE_HDR_SIZE);
|
|
reply_pppoe_hdr->session_id = 0;
|
|
out_length = 0;
|
|
|
|
/* Parse PPPoE tags. */
|
|
in_length = (int)ip_ntohs(pppoe_hdr->length);
|
|
tagp = pppoe_hdr->payload;
|
|
while (in_length >= IPPPP_PPPOE_TAG_HSIZE)
|
|
{
|
|
tag_type = (Ip_u16)IP_GET_NTOHS(&tagp[0]);
|
|
tag_length = (Ip_u16)IP_GET_NTOHS(&tagp[2]);
|
|
in_length -= IPPPP_PPPOE_TAG_HSIZE;
|
|
|
|
if (tag_length > in_length)
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING,
|
|
ethif, IP_NULL, "<ippppoe> :: invalid PPPoE PADX tag length = %d, type = 0x%x [eth %ld]",
|
|
tag_length, tag_type, ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
|
|
switch(tag_type)
|
|
{
|
|
/* Must contain exactly one tag of type 'Service-Name' */
|
|
case IPPPP_PPPOE_TAG_Service_Name:
|
|
/* Verify peer service_name */
|
|
if (service_ok == IP_FALSE)
|
|
{
|
|
Ip_u8 *service_name;
|
|
Ip_u16 service_length;
|
|
|
|
service_name = &tagp[IPPPP_PPPOE_TAG_HSIZE];
|
|
service_length = tag_length;
|
|
|
|
service_ok = IP_TRUE;
|
|
if (service_length > 0)
|
|
{
|
|
Ip_u8 *service_name_l;
|
|
Ip_size_t service_length_l;
|
|
|
|
service_length_l = sizeof(ipppp.buf);
|
|
service_name_l = (Ip_u8 *)ipcom_sysvar_get_conf("ipppp.pppoe.service_name", ipppp.buf, &service_length_l,
|
|
IP_NULL, ethif->name);
|
|
if (service_name_l != IP_NULL && service_length_l > 0)
|
|
{
|
|
if (ipcom_memcmp(service_name, service_name_l,
|
|
IP_MIN(service_length, service_length_l))) /*lint !e670 Possible access beyond array */
|
|
service_ok = IP_FALSE;
|
|
}
|
|
}
|
|
/* if service_name is useable, copy it into the reply and accept no more */
|
|
if (service_ok == IP_TRUE)
|
|
{
|
|
ipcom_memcpy(reply_pppoe_hdr->payload + out_length, tagp, (Ip_size_t)(IPPPP_PPPOE_TAG_HSIZE + tag_length));
|
|
out_length = (Ip_u16)(out_length + IPPPP_PPPOE_TAG_HSIZE + tag_length);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* Copy the tags below to the output packet. */
|
|
case IPPPP_PPPOE_TAG_Host_Uniq:
|
|
case IPPPP_PPPOE_TAG_Relay_Session_Id:
|
|
ipcom_memcpy(reply_pppoe_hdr->payload + out_length, tagp, (Ip_size_t)(IPPPP_PPPOE_TAG_HSIZE + tag_length)); /*lint !e669 Possible data overrun */ /*lint !e670 Possible access beyond array */
|
|
out_length = (Ip_u16)(out_length + IPPPP_PPPOE_TAG_HSIZE + tag_length);
|
|
break;
|
|
|
|
case IPPPP_PPPOE_TAG_AC_Cookie:
|
|
if (pppoe_hdr->code == IPPPP_PPPOE_HDR_CODE_PADR)
|
|
{
|
|
#ifdef IPPPP_PPPOE_USE_MD5
|
|
#ifdef OPENSSL_FIPS
|
|
if (!FIPS_mode())
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING,
|
|
ethif, IP_NULL, "<ippppoe> :: PPPoE ipppp_pppoe_set_cookie with MD5 as OPENSSL FIPS is not enabled");
|
|
#endif
|
|
|
|
if (tag_length == MD5_DIGEST_LENGTH)
|
|
{
|
|
ipppp_pppoe_set_cookie(ipppp.pppoe.gen_cookie, eth_hdr->eth_src);
|
|
if (ipcom_memcmp(ipppp.pppoe.gen_cookie, &tagp[IPPPP_PPPOE_TAG_HSIZE], MD5_DIGEST_LENGTH) == 0) /*lint !e670 Possible access beyond array */
|
|
cookie_ok = IP_TRUE;
|
|
}
|
|
#ifdef OPENSSL_FIPS
|
|
} /* FIPS_mode checking */
|
|
#endif
|
|
#endif /* IPPPP_PPPOE_USE_MD5 */
|
|
#ifdef OPENSSL_FIPS
|
|
#ifdef IPPPP_PPPOE_USE_MD5
|
|
else
|
|
#endif
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING,
|
|
ethif, IP_NULL, "<ippppoe> :: PPPoE ipppp_pppoe_set_cookie with MD5 is disabled as OPENSSL FIPS is enabled");
|
|
if (tag_length == IPPPP_PPPOE_NON_ALGORITHM_LENGTH)
|
|
cookie_ok = IP_TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
else if (pppoe_hdr->code == IPPPP_PPPOE_HDR_CODE_PADO)
|
|
{
|
|
/* Received PADO with cookie -> send it back in PADR */
|
|
ipcom_memcpy(reply_pppoe_hdr->payload + out_length, tagp, (Ip_size_t)(IPPPP_PPPOE_TAG_HSIZE + tag_length)); /*lint !e669 Possible data overrun */ /*lint !e670 Possible access beyond array */
|
|
#ifdef IPPPP_NORMAL_NO_BUILD_WARNINGS
|
|
out_length += (IPPPP_PPPOE_TAG_HSIZE + tag_length);
|
|
#else
|
|
out_length = (Ip_u16)(out_length + (IPPPP_PPPOE_TAG_HSIZE + tag_length));
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case IPPPP_PPPOE_TAG_End_Of_List:
|
|
if (tag_length != 0)
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING,
|
|
ethif, IP_NULL, "<ippppoe> :: PPPoE PADX End_Of_List tag had non-zero length (%d) [eth %ld]",
|
|
tag_length, ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
goto breakout;
|
|
|
|
default:
|
|
/* ignore unknown tags for future backwards compability. */
|
|
break;
|
|
}
|
|
|
|
in_length -= tag_length;
|
|
tagp += (IPPPP_PPPOE_TAG_HSIZE + tag_length);
|
|
}
|
|
|
|
breakout:
|
|
err_status = 0;
|
|
if (service_ok == IP_FALSE)
|
|
{
|
|
ipppp_log(IPCOM_LOG_INFO,
|
|
ethif, IP_NULL, "<ippppoe> :: ignored - missing/unknown Service_Name tag(s) [eth %ld]",
|
|
ethif->ifindex);
|
|
/* Reply with a PADS containing Service-Name-Error.
|
|
err_status = -1;
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, IPPPP_PPPOE_TAG_Service_Name_Error);
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, 0);
|
|
*/
|
|
goto leave;
|
|
}
|
|
|
|
switch (pppoe_hdr->code)
|
|
{
|
|
case IPPPP_PPPOE_HDR_CODE_PADI:
|
|
{
|
|
/* Send PADO */
|
|
char *ac_name;
|
|
Ip_size_t ac_name_len;
|
|
|
|
/* Add AC_Name tag for PADO packet reply */
|
|
ac_name_len = sizeof(ipppp.buf);
|
|
ac_name = ipcom_sysvar_get("ipppp.pppoe.ac_name", ipppp.buf, &ac_name_len);
|
|
if (ac_name == IP_NULL || ac_name_len < 1)
|
|
{
|
|
ipcom_strcpy(ipppp.buf, "UNDEFINED");
|
|
ac_name_len = 9;
|
|
}
|
|
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, IPPPP_PPPOE_TAG_AC_Name);
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, ac_name_len);
|
|
ipcom_strcpy((char *)reply_pppoe_hdr->payload + out_length, ipppp.buf);
|
|
out_length = (Ip_u16)(out_length + ac_name_len);
|
|
|
|
/* Add cookie code to protect vs DOS attacks */
|
|
#ifdef IPPPP_PPPOE_USE_MD5
|
|
#ifdef OPENSSL_FIPS
|
|
if (!FIPS_mode())
|
|
{
|
|
#endif
|
|
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, IPPPP_PPPOE_TAG_AC_Cookie);
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, MD5_DIGEST_LENGTH);
|
|
ipppp_pppoe_set_cookie(reply_pppoe_hdr->payload + out_length, eth_hdr->eth_src);
|
|
out_length = (Ip_u16)(out_length + MD5_DIGEST_LENGTH);
|
|
#ifdef OPENSSL_FIPS
|
|
}
|
|
else
|
|
{
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, IPPPP_PPPOE_TAG_AC_Cookie);
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, IPPPP_PPPOE_NON_ALGORITHM_LENGTH); out_length += IPPPP_PPPOE_NON_ALGORITHM_LENGTH;
|
|
}
|
|
#endif
|
|
#endif /* IPPPP_PPPOE_USE_MD5 */
|
|
|
|
reply_pppoe_hdr->code = IPPPP_PPPOE_HDR_CODE_PADO;
|
|
}
|
|
break;
|
|
|
|
case IPPPP_PPPOE_HDR_CODE_PADR:
|
|
/* Send PADS */
|
|
if (cookie_ok == IP_FALSE)
|
|
{
|
|
ipppp_log(IPCOM_LOG_NOTICE,
|
|
ethif, IP_NULL, "<ippppoe> :: PPPoE PADR - missing or illegal cookie [eth %ld]",
|
|
ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
|
|
/* Check action and correct code for PADOs. */
|
|
if (err_status == 0)
|
|
{
|
|
/* Create PPPoE server interface */
|
|
netif = ipppp_pppoe_if_create_server(ethif, eth_hdr, reply_pppoe_hdr);
|
|
if (netif == IP_NULL)
|
|
{
|
|
ipppp_log(IPCOM_LOG_ERR,
|
|
ethif, IP_NULL, "<ippppoe> :: failed to attach PPPoE session [totses %ld] [numifses %ld] [eth %ld]",
|
|
ipppp.pppoe.num_sessions, ethif_eth->pppoe_num_sessions, ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
ipppp_log(IPCOM_LOG_INFO,
|
|
ethif, IP_NULL, "<ippppoe> :: attaching PPPoE [ses %d] [eth %d] [numifses %d]",
|
|
netif->ifindex, ethif->ifindex, ethif_eth->pppoe_num_sessions);
|
|
err_status = 1;
|
|
}
|
|
reply_pppoe_hdr->code = IPPPP_PPPOE_HDR_CODE_PADS;
|
|
break;
|
|
|
|
case IPPPP_PPPOE_HDR_CODE_PADO:
|
|
case IPPPP_PPPOE_HDR_CODE_PADS:
|
|
/* Received PADO/PADS - PPPoE in client mode */
|
|
netif = ipppp_pppoe_find_netif(ethif->ifindex);
|
|
|
|
if (netif == IP_NULL || netif->type != IP_IFT_PPPOE)
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING,
|
|
ethif, IP_NULL, "<ippppoe> :: received PPPoE PADO/PADS packet with missing PPP interface [eth %ld]",
|
|
ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
pppif = netif->plink;
|
|
|
|
if (pppoe_hdr->code == IPPPP_PPPOE_HDR_CODE_PADO)
|
|
{
|
|
/* Send PADR */
|
|
reply_pppoe_hdr->code = IPPPP_PPPOE_HDR_CODE_PADR;
|
|
|
|
/* Remember the peer Ethernet address so we dont connect to wrong peer */
|
|
ipcom_memcpy(pppif->pppoe_peer_eth_addr, eth_hdr->eth_src, IPPPP_ETH_ADDR_SIZE);
|
|
}
|
|
else
|
|
{
|
|
/* Check Ethernet source address (must match). */
|
|
if (ipcom_memcmp(eth_hdr->eth_src, pppif->pppoe_peer_eth_addr, 6) != 0)
|
|
{
|
|
ipppp_log(IPCOM_LOG_NOTICE,
|
|
ethif, IP_NULL, "<ippppoe> :: PADS - Ethernet source address mismatch (ignoring PADS) [in %s] [ses %ld] [eth %ld]",
|
|
ipppp_pppoe_ascii_addr(eth_hdr->eth_src),
|
|
netif->ifindex, ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
|
|
/* Save peer session_id */
|
|
pppif->pppoe_peer_session_id_n = (Ip_u16)session_id;
|
|
|
|
#ifdef IPPPP_USE_PPPMP
|
|
/* Time to open up a new link. Find which link index is available */
|
|
if (IP_BIT_ISSET(pppif->pppflags, IPPPP_PPPIFFLAG_NEG_MP))
|
|
{
|
|
Ipppp_link * trylink;
|
|
int link_index = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < pppif->data.mp.mp_links; i++)
|
|
{
|
|
trylink = &pppif->link[i];
|
|
|
|
if(IP_BIT_ISFALSE(trylink->data.flags, IPPPP_LINKFLAG_UP))
|
|
{
|
|
link_index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (link_index < 0)
|
|
{
|
|
ipppp_log(IPCOM_LOG_NOTICE,
|
|
ethif, IP_NULL, "<ippppoe> :: %s: no link available",
|
|
netif->name);
|
|
goto leave;
|
|
}
|
|
netif->link_index = link_index;
|
|
}
|
|
/* Store this session id for this link */
|
|
link = &pppif->link[netif->link_index];
|
|
link->data.pppoe_session_id = session_id;
|
|
#endif /* IPPPP_USE_PPPMP */
|
|
|
|
/* One more session. */
|
|
ipppp.pppoe.num_sessions++;
|
|
ethif_eth->pppoe_num_sessions++;
|
|
|
|
/* Received PADS -> start the PPPoE Interface. */
|
|
IPSTACK_TIMEOUT_CANCEL(ethif_eth->pppoe_tmo_handle);
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_LOCK, IP_NULL);
|
|
(void)netif->link_ioevent(netif, IP_EIOXUP, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_UNLOCK, IP_NULL);
|
|
goto leave;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
IP_PANIC();
|
|
break;
|
|
}
|
|
|
|
/* Add optional End-of-List for backwards compatibility. */
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, IPPPP_PPPOE_TAG_End_Of_List);
|
|
IPPPP_PPPOE_TAG_SET16(reply_pppoe_hdr->payload, out_length, 0);
|
|
|
|
/* Ethernet header. */
|
|
ipnet_eth_add_hdr(reply_pkt,
|
|
(void *)ethif,
|
|
eth_hdr->eth_src,
|
|
ethif->link_addr,
|
|
ip_htons(IPPPP_PPPOE_MAC_DIS));
|
|
|
|
/* PPPoE header. */
|
|
reply_pppoe_hdr->ver_type = IPPPP_PPPOE_HDR_VERTYPE;
|
|
reply_pppoe_hdr->length = ip_htons(out_length);
|
|
ipcom_pkt_push_back(reply_pkt, out_length);
|
|
|
|
/* Write the PAD[O|S] reply. */
|
|
ipppp_pppoe_padx_print(ethif, reply_pppoe_hdr, IP_FALSE, (Ip_s32)ethif->ifindex);
|
|
(void)ipcom_ppp_ioevent(ethif, IPPPP_EIOXOUTPUT_IF, reply_pkt);
|
|
reply_pkt = IP_NULL;
|
|
|
|
if (err_status == 1)
|
|
{
|
|
/* Just sent a PADS -> start the PPPoE Interface. */
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_LOCK, IP_NULL);
|
|
(void)ipppp_ioctl(netif, IP_SIOCXOPEN, IP_NULL);
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_UNLOCK, IP_NULL);
|
|
}
|
|
goto leave;
|
|
}
|
|
|
|
case IPPPP_PPPOE_HDR_CODE_PADT: /* PPPoE Active Discovery Terminate packet */
|
|
if (pppoe_hdr->session_id == 0xffff)
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING,
|
|
ethif, IP_NULL, "<ippppoe> :: reserved PPPoE PADR session_id 0xffff [eth %ld]",
|
|
ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
|
|
sessionidul = (unsigned long)session_id;
|
|
ioretul = (unsigned long)ipcom_ppp_ioevent(IP_NULL, IPPPP_EIOXINDEXTONETIF, (void *)sessionidul);
|
|
netif = (Ipcom_netif *)ioretul;
|
|
|
|
if (netif == IP_NULL || netif->type != IP_IFT_PPPOE)
|
|
{
|
|
/* If the peer session id fails check if client interface */
|
|
ifindexul = (unsigned long)ethif_eth->pppoe_ppp_ifindex;
|
|
ioretul = (unsigned long)ipcom_ppp_ioevent(IP_NULL, IPPPP_EIOXINDEXTONETIF, (void *)ifindexul);
|
|
netif = (Ipcom_netif *)ioretul;
|
|
|
|
if (netif == IP_NULL || netif->type != IP_IFT_PPPOE)
|
|
{
|
|
ipppp_log(IPCOM_LOG_NOTICE,
|
|
ethif, IP_NULL, "<ippppoe> :: PPPoE PADT with unknown session-id(%d) [in %s] [eth %ld]",
|
|
session_id,
|
|
ipppp_pppoe_ascii_addr(eth_hdr->eth_src),
|
|
ethif->ifindex);
|
|
goto leave;
|
|
}
|
|
}
|
|
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_LOCK, IP_NULL);
|
|
pppif = netif->plink;
|
|
|
|
/* Check Ethernet source address (must match). */
|
|
if (ipcom_memcmp(eth_hdr->eth_src, pppif->pppoe_peer_eth_addr, 6) != 0)
|
|
{
|
|
ipppp_log(IPCOM_LOG_NOTICE,
|
|
ethif, IP_NULL, "<ippppoe> :: PADT - Ethernet source address mismatch (ignoring PADT) [in %s] [ses %ld] [eth %ld]",
|
|
ipppp_pppoe_ascii_addr(eth_hdr->eth_src),
|
|
netif->ifindex, ethif->ifindex);
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_UNLOCK, IP_NULL);
|
|
goto leave;
|
|
}
|
|
|
|
if (IP_BIT_ISSET(netif->flags, IP_IFF_RUNNING))
|
|
{
|
|
#ifdef IPPPP_USE_PPPMP
|
|
Ipppp_link * this_link;
|
|
int link_index;
|
|
#endif
|
|
|
|
/* Stop the PPPoE interface. */
|
|
ipppp_log(IPCOM_LOG_INFO, ethif, IP_NULL, "<ippppoe> :: stopping PPPoE [ses %ld] [eth %ld] [numifses %ld]",
|
|
netif->ifindex, ethif->ifindex, ethif_eth->pppoe_num_sessions);
|
|
|
|
#ifdef IPPPP_USE_PPPMP
|
|
if ((link_index = ipppp_pppoe_find_link(netif, session_id)) < 0)
|
|
{
|
|
ipppp_log(IPCOM_LOG_WARNING, ethif, IP_NULL,
|
|
"<ippppoe> :: PPPoE session %d does not match an open link.",
|
|
session_id);
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_UNLOCK, IP_NULL);
|
|
goto leave;
|
|
}
|
|
|
|
/* Stop the interface (make sure the link is actually up before doing this) */
|
|
this_link = &pppif->link[link_index];
|
|
if (IP_BIT_ISSET(this_link->data.flags, IPPPP_LINKFLAG_UP))
|
|
(void)ipppp_ioctl(netif, IP_SIOCXCLOSE, IP_NULL);
|
|
#else
|
|
(void)ipppp_ioctl(netif, IP_SIOCXCLOSE, IP_NULL);
|
|
#endif /* IPPPP_USE_PPPMP */
|
|
}
|
|
|
|
(void)ipcom_ppp_ioevent(netif, IPPPP_EIOXIF_UNLOCK, IP_NULL);
|
|
break;
|
|
|
|
default:
|
|
ipppp_log(IPCOM_LOG_WARNING, ethif, IP_NULL, "<ippppoe> :: unknown PPPoE code 0x%x [eth %ld]",
|
|
pppoe_hdr->code, ethif->ifindex);
|
|
break;
|
|
}
|
|
|
|
leave:
|
|
ipcom_pkt_free(pkt);
|
|
if (reply_pkt != IP_NULL)
|
|
ipcom_pkt_free(reply_pkt);
|
|
|
|
/* Restore virtual router index */
|
|
if (vr != pkt->vr_index)
|
|
ipcom_proc_vr_set(vr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
#else
|
|
int ipppp_pppoe_empty_file;
|
|
#endif /* IPPPP_USE_PPPOE */
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|