/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 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 #include #include #include #include #include "ipppp_h.h" #ifdef IPNET #include #include #elif defined(IPLITE) #include #else #error MUST_CHOOSE_STACK #endif #ifdef OPENSSL_FIPS #include #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, " :: 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, " :: %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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: %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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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, " :: 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 **************************************************************************** */