/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include "../../../Family/if_ppplink.h" #include "../../../Family/ppp_domain.h" #include "PPPoE.h" #include "pppoe_rfc.h" #include "pppoe_dlil.h" /* ----------------------------------------------------------------------------- Definitions ----------------------------------------------------------------------------- */ //#define PPPENET_COMPAT 1 #define PPPOE_VER 1 #define PPPOE_TYPE 1 #define PPPOE_PADI 0x9 #define PPPOE_PADO 0x7 #define PPPOE_PADR 0x19 #define PPPOE_PADS 0x65 #define PPPOE_PADT 0xa7 #define PPPOE_TAG_END_OF_LIST 0x0000 #define PPPOE_TAG_SERVICE_NAME 0x0101 #define PPPOE_TAG_AC_NAME 0x0102 #define PPPOE_TAG_HOST_UNIQ 0x0103 #define PPPOE_TAG_AC_COOKIE 0x0104 #define PPPOE_TAG_VENDOR_SPECIFIC 0x0105 #define PPPOE_TAG_RELAY_SESSION_ID 0x0110 #define PPPOE_TAG_SERVICE_NAME_ERROR 0x0201 #define PPPOE_TAG_AC_SYSTEM_ERROR 0x0202 #define PPPOE_TAG_GENERIC_ERROR 0x0203 #define PPPOE_TIMER_CONNECT 20 // let's have a connect timer of 20 seconds #define PPPOE_TIMER_RING 30 // let's have a ring timer of 30 seconds #define PPPOE_TIMER_RETRY 3 // let's have a retry period of 3 seconds #define SERVER_NAME "Darwin\0" #define SERVICE_NAME "Think-Different\0" #define PPPOE_HOST_UNIQ_LEN 64 // 64 bytes (Fix Me: dynamically allocate tag) #define PPPOE_RELAY_ID_LEN 64 // 64 bytes (Fix Me: dynamically allocate tag) #define PPPOE_AC_COOKIE_LEN 64 // 64 bytes (Fix Me: dynamically allocate tag) #define PPPOE_TMPBUF_SIZE 1500 struct pppoe { u_int8_t ver:4; u_int8_t typ:4; u_int8_t code; u_int16_t sessid; u_int16_t len; }; // a pppoe_tag is basically a buffer with information how much data it contains // right now, and how much space it provides in total. struct pppoe_tag { u_int16_t len; /* data length */ u_int16_t max_len; /* buffer size */ u_int8_t *data; /* pointer to actual data */ }; // utility macro that makes it easy to declare a pppoe_tag with static data #define PPPOE_TAG(name, size) \ struct pppoe_tag name; \ u_int8_t name##_BUF[size] // utility macro to setup a pppoe_tag struct #define PPPOE_TAG_SETUP(name) \ do { \ name.data = name##_BUF; \ name.max_len = sizeof(name##_BUF); \ name.len = 0; \ name.data[0] = 0; \ } while (0) // utility macro to reset pppoe_tag struct after cloning #define PPPOE_TAG_RESETUP(name) \ do { \ name.data = name##_BUF; \ } while (0) // utility macro to make it easy to initialize a pppoe_tag with a C string. // This macro also makes sure that no overflow can occur. #define PPPOE_TAG_STRCPY(tag, str) \ do { \ strncpy(tag.data, str, tag.max_len); \ tag.len = strlen(tag.data); \ } while (0) // utility macro that allows to easily compare two pppoe_tag structs. #define PPPOE_TAG_CMP(tag1, tag2) \ ((tag1.len == tag2.len) ? bcmp(tag1.data, tag2.data, tag1.len) : 1) struct pppoe_rfc { // administrative info TAILQ_ENTRY(pppoe_rfc) next; void *host; /* pointer back to the hosting structure */ ifnet_t ifp; /* associated datalink attachment */ pppoe_rfc_input_callback inputcb; /* callback function when data are present */ pppoe_rfc_event_callback eventcb; /* callback function for events */ u_int16_t unit; /* associated interface unit number */ u_int32_t flags; /* is client in loopback mode ? */ u_int32_t uniqueid; /* unique id (currently used for loopback) */ // current state u_int16_t state; /* PPPoE current state */ // outgoing call PPPOE_TAG(ac_name, PPPOE_AC_NAME_LEN); /* Access Concentrator we want to reach */ PPPOE_TAG(service, PPPOE_SERVICE_LEN); /* Service name we want to reach */ u_int16_t timer_connect_setup; /* number of seconds to allow for an outgoing call */ u_int16_t timer_retry_setup; /* number of seconds between retries */ u_int16_t timer_connect; /* seconds remaining before aborting outgoing call */ u_int16_t timer_connect_resend; /* seconds when needs teo start retransmitting */ // incoming call PPPOE_TAG(serv_ac_name, PPPOE_AC_NAME_LEN); /* Access Concentrator we offer */ PPPOE_TAG(serv_service, PPPOE_SERVICE_LEN); /* Service name we offer */ u_int16_t timer_ring_setup; /* number of seconds to allow for an incoming call */ u_int16_t timer_ring; /* second remaining before aborting incoming call */ // commom outgoing/incoming call PPPOE_TAG(host_uniq, PPPOE_HOST_UNIQ_LEN); /* client reserved cookie */ PPPOE_TAG(ac_cookie, PPPOE_AC_COOKIE_LEN); /* access concentrator reserved cookie */ PPPOE_TAG(relay_id, PPPOE_RELAY_ID_LEN); /* intermediate relay cookie */ u_int16_t session_id; /* session id between client and server */ u_int8_t peer_address[ETHER_ADDR_LEN]; /* ethernet address we are connected to */ }; /* ----------------------------------------------------------------------------- Globals ----------------------------------------------------------------------------- */ // change the algo to remove that var u_int8_t pppenet_tmpbuf[PPPOE_TMPBUF_SIZE]; u_int16_t pppoe_unique_session_id = 1; u_int32_t pppoe_unique_address = 1; TAILQ_HEAD(, pppoe_rfc) pppoe_rfc_head; extern lck_mtx_t *ppp_domain_mutex; /* ----------------------------------------------------------------------------- Forward declarations ----------------------------------------------------------------------------- */ static u_int16_t handle_PADI(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from); static u_int16_t handle_PADR(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from); static u_int16_t handle_PADO(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from); static u_int16_t handle_PADS(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from); static u_int16_t handle_PADT(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from); static u_int16_t handle_data(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from); static u_int16_t handle_ctrl(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from); static void send_event(struct pppoe_rfc *rfc, u_int32_t event, u_int32_t msg); static void send_PAD(struct pppoe_rfc *rfc, u_int8_t *address, u_int16_t code, u_int16_t sessid, struct pppoe_tag *ac_name, struct pppoe_tag *service, struct pppoe_tag *host_uniq, struct pppoe_tag *ac_cookie, struct pppoe_tag *relay_id); static u_int16_t add_tag(u_int8_t *data, u_int16_t tag, struct pppoe_tag *val); static u_int16_t get_tag(mbuf_t m, u_int16_t tag, struct pppoe_tag *val); u_int16_t pppoe_rfc_input(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from, u_int16_t typ); void pppoe_rfc_lower_output(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *to, u_int16_t typ); /* ----------------------------------------------------------------------------- intialize pppoe protocol on ethernet unit need to change it to handle multiple ethernet interfaces ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_init() { pppoe_dlil_init(); TAILQ_INIT(&pppoe_rfc_head); return 0; } /* ----------------------------------------------------------------------------- dispose of a pppoe protocol ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_dispose() { if (pppoe_dlil_dispose()) return 1; return 0; } /* ----------------------------------------------------------------------------- intialize a new pppoe structure ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_new_client(void *host, void **data, pppoe_rfc_input_callback input, pppoe_rfc_event_callback event) { struct pppoe_rfc *rfc; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); rfc = (struct pppoe_rfc *)_MALLOC(sizeof (struct pppoe_rfc), M_TEMP, M_WAITOK); if (rfc == 0) return 1; //log(LOGVAL, "PPPoE new_client rfc = 0x%x\n", rfc); bzero(rfc, sizeof(struct pppoe_rfc)); rfc->host = host; rfc->unit = 0xFFFF; rfc->uniqueid = pppoe_unique_address++; rfc->inputcb = input; rfc->eventcb = event; rfc->timer_connect_setup = PPPOE_TIMER_CONNECT; rfc->timer_ring_setup = PPPOE_TIMER_RING; rfc->timer_retry_setup = PPPOE_TIMER_RETRY; rfc->state = PPPOE_STATE_DISCONNECTED; PPPOE_TAG_SETUP(rfc->ac_name); PPPOE_TAG_SETUP(rfc->service); PPPOE_TAG_SETUP(rfc->serv_ac_name); PPPOE_TAG_SETUP(rfc->serv_service); PPPOE_TAG_SETUP(rfc->host_uniq); PPPOE_TAG_SETUP(rfc->ac_cookie); PPPOE_TAG_SETUP(rfc->relay_id); PPPOE_TAG_STRCPY(rfc->serv_ac_name, SERVER_NAME); PPPOE_TAG_STRCPY(rfc->serv_service, SERVICE_NAME); *data = rfc; TAILQ_INSERT_TAIL(&pppoe_rfc_head, rfc, next); return 0; } /* ----------------------------------------------------------------------------- dispose of a pppoe structure ----------------------------------------------------------------------------- */ void pppoe_rfc_free_client(void *data) { struct pppoe_rfc *rfc = (struct pppoe_rfc *)data; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE free (0x%x)\n", rfc); if (rfc) { if (rfc->ifp) pppoe_dlil_detach(rfc->ifp); TAILQ_REMOVE(&pppoe_rfc_head, rfc, next); _FREE(rfc, M_TEMP); } } /* ----------------------------------------------------------------------------- connect the protocol to the service 'num' ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_connect(void *data, u_int8_t *ac_name, u_int8_t *service) { struct pppoe_rfc *rfc = (struct pppoe_rfc *)data; u_int8_t broadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; u_int8_t emptyaddr[ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 }; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (rfc->state != PPPOE_STATE_DISCONNECTED) return 1; if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE connect (0x%x): ac-name = '%s', service = '%s', \n", rfc, ac_name, service); if (rfc->unit == 0xFFFF) { // not attached to an interface yet. Use unit 0, for conveniency. // an application that doesn't specify the unit will use unit 0, if available. if (pppoe_dlil_attach(0, &rfc->ifp)) return 1; rfc->unit = 0; } PPPOE_TAG_STRCPY(rfc->ac_name, ac_name); PPPOE_TAG_STRCPY(rfc->service, service); // use our rfc address for the uniq id, we now it won't change since we allocate ourserlves // FIXME sizeof(u_int32_t) might be != sizeof(pointer) *(u_int32_t *)&rfc->host_uniq.data[0] = (u_int32_t)rfc; rfc->host_uniq.len = sizeof(u_int32_t); rfc->ac_cookie.len = 0; rfc->relay_id.len = 0; rfc->state = PPPOE_STATE_LOOKING; rfc->timer_connect = rfc->timer_connect_setup; // resend PADI/PADR every PPPOE_TIMEOUT_RETRY seconds rfc->timer_connect_resend = rfc->timer_connect - rfc->timer_retry_setup; if (!bcmp(rfc->peer_address, emptyaddr, ETHER_ADDR_LEN)) bcopy(broadcastaddr, rfc->peer_address, ETHER_ADDR_LEN); // if ac-name specified, try to reach it, otherwise, don't use name // may be shoult use a '*' semantic in the address ? send_PAD(rfc, rfc->peer_address, PPPOE_PADI, 0, &rfc->ac_name, &rfc->service, &rfc->host_uniq, 0, 0); return 0; } /* ----------------------------------------------------------------------------- set server and service name ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_bind(void *data, u_int8_t *ac_name, u_int8_t *service) { struct pppoe_rfc *rfc = (struct pppoe_rfc *)data; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE bind (0x%x): ac-name = '%s', service = '%s'\n", rfc, ac_name ? (char*)ac_name : "nul", service ? (char*)service : "nul"); if (ac_name) PPPOE_TAG_STRCPY(rfc->serv_ac_name, ac_name); if (service) PPPOE_TAG_STRCPY(rfc->serv_service, service); return 0; } /* ----------------------------------------------------------------------------- accept an incoming connection ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_accept(void *data) { struct pppoe_rfc *rfc = (struct pppoe_rfc *)data; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (rfc->state != PPPOE_STATE_RINGING) return 1; if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE accept (0x%x)\n", rfc); // should check wih other numbers currently sets with active sessions rfc->session_id = pppoe_unique_session_id++; // generate a session id // host_uniq and rfc->relay_session_id have been got from the previous PADR send_PAD(rfc, rfc->peer_address, PPPOE_PADS, rfc->session_id, &rfc->ac_name, &rfc->service, rfc->host_uniq.len ? &rfc->host_uniq : 0, 0, rfc->relay_id.len ? &rfc->relay_id : 0); rfc->state = PPPOE_STATE_CONNECTED; send_event(rfc, PPPOE_EVT_CONNECTED, 0); return 0; } /* ----------------------------------------------------------------------------- listen for incoming calls ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_listen(void *data) { struct pppoe_rfc *rfc = (struct pppoe_rfc *)data; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (rfc->state != PPPOE_STATE_DISCONNECTED) return 1; if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE listen (0x%x)\n", rfc); if (rfc->unit == 0xFFFF) { // not attached to an interface yet. Use unit 0, for conveniency. // an application that doesn't specify the unit will use unit 0, if available. if (pppoe_dlil_attach(0, &rfc->ifp)) return 1; rfc->unit = 0; } rfc->state = PPPOE_STATE_LISTENING; return 0; } /* ----------------------------------------------------------------------------- abort current connection, depend on the state ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_abort(void *data, int evt_enable) { struct pppoe_rfc *rfc = (struct pppoe_rfc *)data; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE abort (0x%x)\n", rfc); switch (rfc->state) { case PPPOE_STATE_LOOKING: case PPPOE_STATE_CONNECTING: case PPPOE_STATE_LISTENING: case PPPOE_STATE_RINGING: rfc->state = PPPOE_STATE_DISCONNECTED; bzero(rfc->peer_address, sizeof(rfc->peer_address)); if (evt_enable) send_event(rfc, PPPOE_EVT_DISCONNECTED, 0); break; case PPPOE_STATE_CONNECTED: pppoe_rfc_disconnect(data); break; case PPPOE_STATE_DISCONNECTED: break; } return 0; } /* ----------------------------------------------------------------------------- disconnect pppoe (only applies to connected state) otherwise, should call abort ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_disconnect(void *data) { struct pppoe_rfc *rfc = (struct pppoe_rfc *)data; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (rfc->state != PPPOE_STATE_CONNECTED) { return 1; } if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE disconnect (0x%x)\n", rfc); send_PAD(rfc, rfc->peer_address, PPPOE_PADT, rfc->session_id, 0, 0, 0, 0, 0); rfc->state = PPPOE_STATE_DISCONNECTED; bzero(rfc->peer_address, sizeof(rfc->peer_address)); send_event(rfc, PPPOE_EVT_DISCONNECTED, 0); return 0; } /* ----------------------------------------------------------------------------- clone the two data structures NOTE : the host field is NOT copied, and rfc needs to be relinked ----------------------------------------------------------------------------- */ void pppoe_rfc_clone(void *data1, void *data2) { struct pppoe_rfc *rfc2 = (struct pppoe_rfc *)data2; void *host2; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); host2 = rfc2->host; if (rfc2->ifp) pppoe_dlil_detach(rfc2->ifp); TAILQ_REMOVE(&pppoe_rfc_head, rfc2, next); bcopy(data1, data2, sizeof(struct pppoe_rfc)); rfc2->host = host2; TAILQ_INSERT_TAIL(&pppoe_rfc_head, rfc2, next); // cannot fail, there is no attachment done, and it's is just refcnt bumping if (rfc2->ifp) pppoe_dlil_attach(rfc2->unit, &rfc2->ifp); // make the pointers point to the right place PPPOE_TAG_RESETUP(rfc2->ac_name); PPPOE_TAG_RESETUP(rfc2->service); PPPOE_TAG_RESETUP(rfc2->serv_ac_name); PPPOE_TAG_RESETUP(rfc2->serv_service); PPPOE_TAG_RESETUP(rfc2->host_uniq); PPPOE_TAG_RESETUP(rfc2->ac_cookie); PPPOE_TAG_RESETUP(rfc2->relay_id); } /* ----------------------------------------------------------------------------- called by protocol family when slowtiemr expires check the active timers and fires them appropriatly refaire les timers ----------------------------------------------------------------------------- */ void pppoe_rfc_timer() { struct pppoe_rfc *rfc; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); TAILQ_FOREACH(rfc, &pppoe_rfc_head, next) { switch (rfc->state) { case PPPOE_STATE_LOOKING: case PPPOE_STATE_CONNECTING: if (rfc->timer_connect == 0) { if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE timer (0x%x): CONNECT_TIMER expires\n", rfc); rfc->state = PPPOE_STATE_DISCONNECTED; bzero(rfc->peer_address, sizeof(rfc->peer_address)); // double-check for the error number ? send_event(rfc, PPPOE_EVT_DISCONNECTED, PPPOE_STATE_LOOKING ? EHOSTUNREACH : ECONNREFUSED); break; } if (rfc->timer_connect == rfc->timer_connect_resend) { rfc->timer_connect_resend -= rfc->timer_retry_setup; send_PAD(rfc, rfc->peer_address, rfc->state == PPPOE_STATE_LOOKING ? PPPOE_PADI : PPPOE_PADR, 0, rfc->ac_name.len ? &rfc->ac_name : 0, &rfc->service, &rfc->host_uniq, rfc->ac_cookie.len ? &rfc->ac_cookie : 0, rfc->relay_id.len ? &rfc->relay_id : 0); } rfc->timer_connect--; break; case PPPOE_STATE_RINGING: if (rfc->timer_ring == 0) { if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE timer (0x%x): RING_TIMER expires\n", rfc); rfc->state = PPPOE_STATE_DISCONNECTED; bzero(rfc->peer_address, sizeof(rfc->peer_address)); send_event(rfc, PPPOE_EVT_DISCONNECTED, 0); break; } rfc->timer_ring--; break; } } } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_output(void *data, mbuf_t m) { struct pppoe_rfc *rfc = (struct pppoe_rfc *)data; u_int8_t *d; struct pppoe *p; mbuf_t m0; u_int16_t skip, len; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); len = 0; for (m0 = m; m0 != 0; m0 = mbuf_next(m0)) len += mbuf_len(m0); if (rfc->state != PPPOE_STATE_CONNECTED) return ENXIO; // log(LOGVAL, "PPPoE write, len = %d\n", len); //d = mtod(m, u_int8_t *); //log(LOGVAL, "PPPoE write, data = %x %x %x %x %x %x \n", d[0], d[1], d[2], d[3], d[4], d[5]); skip = 0; #if 0 // FF03 is not sent and ACFC option MUST not be negociated // test anyway if we start with FF03 d = mtod(m, u_int8_t *); if (d[0] == 0xFF && d[1] == 3) skip = 2; #endif mbuf_prepend(&m, sizeof(struct pppoe) - skip , MBUF_WAITOK); d = mbuf_data(m); mbuf_setflags(m, mbuf_flags(m) | MBUF_PKTHDR); p = (struct pppoe *)d; p->ver = PPPOE_VER; p->typ = PPPOE_TYPE; p->code = 0; p->sessid = htons(rfc->session_id); p->len = htons(len - skip); // tag len mbuf_pkthdr_setlen(m, len + sizeof(struct pppoe) - skip); pppoe_rfc_lower_output(rfc, m, rfc->peer_address, PPPOE_ETHERTYPE_DATA); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_command(void *data, u_int32_t cmd, void *cmddata) { struct pppoe_rfc *rfc = (struct pppoe_rfc *)data; u_int16_t unit, error = 0; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); switch (cmd) { case PPPOE_CMD_SETFLAGS: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): set flags = 0x%x\n", rfc, *(u_int32_t *)cmddata); rfc->flags = *(u_int32_t *)cmddata; break; case PPPOE_CMD_GETFLAGS: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): get flags = 0x%x\n", rfc, rfc->flags); *(u_int32_t *)cmddata = rfc->flags; break; case PPPOE_CMD_SETUNIT: unit = *(u_int16_t *)cmddata; if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): set interface unit = %d\n", rfc, unit); if (rfc->unit != unit) { if (rfc->ifp) { pppoe_dlil_detach(rfc->ifp); rfc->ifp = 0; rfc->unit = 0xFFFF; } if (unit != 0xFFFF) { if (pppoe_dlil_attach(unit, &rfc->ifp)) return 1; rfc->unit = unit; } } break; case PPPOE_CMD_GETUNIT: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): get interface unit = %d\n", rfc, rfc->unit); *(u_int16_t *)cmddata = rfc->unit; break; case PPPOE_CMD_GETCONNECTTIMER: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): get connect timer = %d\n", rfc, rfc->timer_connect_setup); *(u_int16_t *)cmddata = rfc->timer_connect_setup; break; case PPPOE_CMD_SETCONNECTTIMER: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): set connect timer = %d\n", rfc, *(u_int16_t *)cmddata); rfc->timer_connect_setup = *(u_int16_t *)cmddata; break; case PPPOE_CMD_GETRINGTIMER: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): get ring timer = %d\n", rfc, rfc->timer_ring_setup); *(u_int16_t *)cmddata = rfc->timer_ring_setup; break; case PPPOE_CMD_SETRINGTIMER: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): set ring timer = %d\n", rfc, *(u_int16_t *)cmddata); rfc->timer_ring_setup = *(u_int16_t *)cmddata; break; case PPPOE_CMD_GETRETRYTIMER: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): get retry timer = %d\n", rfc, rfc->timer_retry_setup); *(u_int16_t *)cmddata = rfc->timer_retry_setup; break; case PPPOE_CMD_SETRETRYTIMER: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): set retry timer = %d\n", rfc, *(u_int16_t *)cmddata); rfc->timer_retry_setup = *(u_int16_t *)cmddata; break; // set the peer address to specify an ethernet address to connect to // must be called before every connect case PPPOE_CMD_SETPEERADDR: if (rfc->flags & PPPOE_FLAG_DEBUG) { u_char *p = cmddata; log(LOGVAL, "PPPoE command (0x%x): set peer ethernet address = %x:%x:%x:%x:%x:%x\n", rfc, p[0], p[1], p[2], p[3], p[4], p[5]); } if (rfc->state != PPPOE_STATE_DISCONNECTED) { error = 1; break; } bcopy(cmddata, rfc->peer_address, ETHER_ADDR_LEN); break; // return the ethernet address we are connected to // broadcast and zero-address are treated the same way. case PPPOE_CMD_GETPEERADDR: if (rfc->flags & PPPOE_FLAG_DEBUG) { u_char *p = rfc->peer_address; log(LOGVAL, "PPPoE command (0x%x): get peer ethernet address = %x:%x:%x:%x:%x:%x\n", rfc, p[0], p[1], p[2], p[3], p[4], p[5]); } bcopy(rfc->peer_address, cmddata, ETHER_ADDR_LEN); break; default: if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE command (0x%x): unknown command = %d\n", rfc, cmd); } return error; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ u_int16_t pppoe_rfc_input(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from, u_int16_t typ) { //log(LOGVAL, "PPPoE input, rfc = 0x%x\n", rfc); lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (mbuf_len(m) < sizeof(struct pppoe)) return 0; // packet too short switch (typ) { case PPPOE_ETHERTYPE_CTRL: return handle_ctrl(rfc, m, from); case PPPOE_ETHERTYPE_DATA: return handle_data(rfc, m, from); } return 0; } /* ----------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------- private functions ----------------------------------- -------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- send an event up to our cliente ----------------------------------------------------------------------------- */ void send_event(struct pppoe_rfc *rfc, u_int32_t event, u_int32_t msg) { if (rfc->flags & PPPOE_FLAG_DEBUG) { char *text = ""; switch (event) { case PPPOE_EVT_RINGING: text = "RINGING"; break; case PPPOE_EVT_CONNECTED: text = "CONNECTED"; break; case PPPOE_EVT_DISCONNECTED: text = "DISCONNECTED"; break; } log(LOGVAL, "PPPoE event %s (0x%x)\n", text, rfc); } if (rfc->eventcb) (*rfc->eventcb)(rfc->host, event, msg); } /* ----------------------------------------------------------------------------- get the value for the tag maxlen contains the max size of name (including null terminator) return 1 if the tag was found and it could fit with maxlen, 0 otherwise ----------------------------------------------------------------------------- */ u_int16_t get_tag(mbuf_t m, u_int16_t tag, struct pppoe_tag *val) { u_int8_t *data, *tmpbuf; struct pppoe *p; u_int16_t totallen, copylen, len; data = mbuf_data(m); p = (struct pppoe *)data; totallen = ntohs(p->len); // Prevent buffer overflow - the PPPoE RFC gurantees us a maximum packet size, however, an attacker // might very well produce huge packets. if (totallen > PPPOE_TMPBUF_SIZE) totallen = PPPOE_TMPBUF_SIZE; copylen = MIN(totallen, mbuf_len(m) - sizeof(struct pppoe)); tmpbuf = &pppenet_tmpbuf[0]; bcopy(data + sizeof(struct pppoe), tmpbuf, copylen); for (m = mbuf_next(m); m && (copylen < totallen); m = mbuf_next(m)) { if (mbuf_len(m) == 0) continue; len = MIN(totallen - copylen, mbuf_len(m)); data = mbuf_data(m); bcopy(data, tmpbuf + copylen, len); copylen += len; } totallen = copylen; data = tmpbuf; val->len = 0; while (totallen > 0) { len = ntohs(*(u_int16_t *)(data + 2)); if (len > totallen) break; // bogus packet //log(LOGVAL, "tag 0x%x 0x%x 0x%x 0x%x \n", data[0], data[1], data[2], data[3]); if (ntohs(*(u_int16_t *)data) == tag) { data += 2; if (len <= val->max_len) { bcopy(data + 2, val->data, len); val->len = len; if (val->len < val->max_len) val->data[val->len] = 0; return 1; } //log(LOGVAL, "got PADO : %s\n", name); break; } else { totallen -= 4 + len; data += 4 + len; } } return 0; } /* ----------------------------------------------------------------------------- add a tag to the data, return the len added ----------------------------------------------------------------------------- */ u_int16_t add_tag(u_int8_t *data, u_int16_t tag, struct pppoe_tag *val) { *(u_int16_t *)data = htons(tag); data += 2; *(u_int16_t *)data = htons(val->len); data += 2; bcopy(val->data, data, val->len); return (val->len + 4); } /* ----------------------------------------------------------------------------- address MUST be a valid ethernet address (6 bytes length) ----------------------------------------------------------------------------- */ void send_PAD(struct pppoe_rfc *rfc, u_int8_t *address, u_int16_t code, u_int16_t sessid, struct pppoe_tag *ac_name, struct pppoe_tag *service, struct pppoe_tag *host_uniq, struct pppoe_tag *ac_cookie, struct pppoe_tag *relay_id) { mbuf_t m = 0; u_int8_t *data; u_int16_t len; struct pppoe *p; if (mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &m) != 0) return; if (mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &m) != 0) { mbuf_freem(m); return; } //MH_ALIGN(m, sizeof(struct pppoe) + namelen + 4 + servlen + 4); data = mbuf_data(m); p = (struct pppoe *)data; p->ver = PPPOE_VER; p->typ = PPPOE_TYPE; p->code = code; p->sessid = htons(sessid); p->len = 0; data += sizeof(struct pppoe); if (service) { len = add_tag(data, PPPOE_TAG_SERVICE_NAME, service); p->len += len; data += len; } if (ac_name) { len = add_tag(data, PPPOE_TAG_AC_NAME, ac_name); p->len += len; data += len; } if (host_uniq) { len = add_tag(data, PPPOE_TAG_HOST_UNIQ, host_uniq); p->len += len; data += len; } if (ac_cookie) { len = add_tag(data, PPPOE_TAG_AC_COOKIE, ac_cookie); p->len += len; data += len; } if (relay_id) { len = add_tag(data, PPPOE_TAG_RELAY_SESSION_ID, relay_id); p->len += len; data += len; } mbuf_setlen(m, sizeof(struct pppoe) + p->len); mbuf_pkthdr_setlen(m, sizeof(struct pppoe) + p->len); p->len = htons(p->len); pppoe_rfc_lower_output(rfc, m, address, PPPOE_ETHERTYPE_CTRL); } /* ----------------------------------------------------------------------------- m contains ethernet header and the actual ethernet data from MUST be a valid ethernet address (6 bytes length) ----------------------------------------------------------------------------- */ u_int16_t handle_PADI(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from) { PPPOE_TAG(name, PPPOE_AC_NAME_LEN); PPPOE_TAG(service, PPPOE_SERVICE_LEN); PPPOE_TAG(hostuniq, PPPOE_HOST_UNIQ_LEN); PPPOE_TAG(relay, PPPOE_RELAY_ID_LEN); if (rfc->state != PPPOE_STATE_LISTENING) return 0; PPPOE_TAG_SETUP(name); PPPOE_TAG_SETUP(service); PPPOE_TAG_SETUP(hostuniq); PPPOE_TAG_SETUP(relay); get_tag(m, PPPOE_TAG_AC_NAME, &name); get_tag(m, PPPOE_TAG_SERVICE_NAME, &service); if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE receive PADI (0x%x): requested service\\name = '%.64s\\%.64s', our service\\name = '%.64s\\%.64s'\n", rfc, service.data, name.data, rfc->serv_service.data, rfc->serv_ac_name.data); // if the client does not specify any name, we must offer ther service // if the client does not specify the service, we still accept it, but don't advertise // any service in the reply if ((!name.len || !PPPOE_TAG_CMP(name, rfc->serv_ac_name)) && (!service.len || !PPPOE_TAG_CMP(service, rfc->serv_service))) { get_tag(m, PPPOE_TAG_HOST_UNIQ, &hostuniq); get_tag(m, PPPOE_TAG_RELAY_SESSION_ID, &relay); // could generate and use ac-cookie, but in that case we would need to change state // do it later... send_PAD(rfc, from, PPPOE_PADO, 0, &rfc->serv_ac_name, service.len ? &service : 0, hostuniq.len ? &hostuniq : 0, 0, relay.len ? &relay : 0); return 1; } return 0; } /* ----------------------------------------------------------------------------- m contains ethernet header and the actual ethernet data from MUST be a valid ethernet address (6 bytes length) ----------------------------------------------------------------------------- */ u_int16_t handle_PADO(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from) { PPPOE_TAG(name, PPPOE_AC_NAME_LEN); PPPOE_TAG(service, PPPOE_SERVICE_LEN); PPPOE_TAG(hostuniq, PPPOE_HOST_UNIQ_LEN); if (rfc->state != PPPOE_STATE_LOOKING) return 0; PPPOE_TAG_SETUP(name); PPPOE_TAG_SETUP(service); PPPOE_TAG_SETUP(hostuniq); get_tag(m, PPPOE_TAG_AC_NAME, &name); get_tag(m, PPPOE_TAG_SERVICE_NAME, &service); if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE receive PADO (0x%x): offered service\\name = '%.64s\\%.64s', expected service\\name = '%.64s\\%.64s'\n", rfc, service.data, name.data, rfc->service.data, rfc->ac_name.data); // since we sent PPPOE_TAG_HOST_UNIQ in our PADI, the tag MUST be present in this PADO get_tag(m, PPPOE_TAG_HOST_UNIQ, &hostuniq); // the connecting rfc is identified by our host_uniq value // check if the ac-name and ac-service match our expectations #ifndef PPPENET_COMPAT if (!PPPOE_TAG_CMP(hostuniq, rfc->host_uniq) && (!rfc->ac_name.len || !PPPOE_TAG_CMP(name, rfc->ac_name)) && (!rfc->service.len || !PPPOE_TAG_CMP(service, rfc->service)) ) { #endif get_tag(m, PPPOE_TAG_AC_COOKIE, &rfc->ac_cookie); get_tag(m, PPPOE_TAG_RELAY_SESSION_ID, &rfc->relay_id); bcopy(from, rfc->peer_address, ETHER_ADDR_LEN); if (rfc->flags & PPPOE_FLAG_PROBE) { // this is not a real connection, // we just performed a probe to detect the presence of a PPPoE servers send_event(rfc, PPPOE_EVT_CONNECTED, 0); return 1; } // resend PADI/PADR every PPPOE_TIMEOUT_RETRY seconds rfc->timer_connect_resend = rfc->timer_connect - rfc->timer_retry_setup; send_PAD(rfc, rfc->peer_address, PPPOE_PADR, 0, rfc->ac_name.len ? &rfc->ac_name : 0, &rfc->service, &rfc->host_uniq, rfc->ac_cookie.len ? &rfc->ac_cookie : 0, rfc->relay_id.len ? &rfc->relay_id : 0); rfc->state = PPPOE_STATE_CONNECTING; return 1; #ifndef PPPENET_COMPAT } #endif return 0; } /* ----------------------------------------------------------------------------- m contains ethernet header and the actual ethernet data from MUST be a valid ethernet address (6 bytes length) ----------------------------------------------------------------------------- */ u_int16_t handle_PADR(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from) { PPPOE_TAG(name, PPPOE_AC_NAME_LEN); PPPOE_TAG(service, PPPOE_SERVICE_LEN); if (rfc->state != PPPOE_STATE_LISTENING) return 0; PPPOE_TAG_SETUP(name); PPPOE_TAG_SETUP(service); get_tag(m, PPPOE_TAG_AC_NAME, &name); get_tag(m, PPPOE_TAG_SERVICE_NAME, &service); if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE receive PADR (0x%x): requested service\\name = '%.64s\\%.64s', our service\\name = '%.64s\\%.64s'\n", rfc, service.data, name.data, rfc->serv_service.data, rfc->serv_ac_name.data); // if the client does not specify any name, we must offer ther service // if the client does not specify the service, still accept it ? if ((!name.len || !PPPOE_TAG_CMP(name, rfc->serv_ac_name)) && (!service.len || !PPPOE_TAG_CMP(service, rfc->serv_service))) { bcopy(from, rfc->peer_address, ETHER_ADDR_LEN); get_tag(m, PPPOE_TAG_HOST_UNIQ, &rfc->host_uniq); get_tag(m, PPPOE_TAG_RELAY_SESSION_ID, &rfc->relay_id); // change the state, so there is no other client trying to call... rfc->timer_ring = rfc->timer_ring_setup; rfc->state = PPPOE_STATE_RINGING; send_event(rfc, PPPOE_EVT_RINGING, 0); // only ring to the first client that matches... return 1; } return 0; } /* ----------------------------------------------------------------------------- m contains ethernet header and the actual ethernet data from MUST be a valid ethernet address (6 bytes length) ----------------------------------------------------------------------------- */ u_int16_t handle_PADS(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from) { PPPOE_TAG(hostuniq, PPPOE_HOST_UNIQ_LEN); struct pppoe *p = mbuf_data(m); u_int16_t sessid = ntohs(p->sessid); if (rfc->state != PPPOE_STATE_CONNECTING) return 0; PPPOE_TAG_SETUP(hostuniq); if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE receive PADS (0x%x): session id = 0x%x\n", rfc, sessid); // since we sent PPPOE_TAG_HOST_UNIQ in our PADR, the tag MUST be present in this PADS get_tag(m, PPPOE_TAG_HOST_UNIQ, &hostuniq); // the connecting rfc is identified by our host_uniq value #ifndef PPPENET_COMPAT if (!PPPOE_TAG_CMP(hostuniq, rfc->host_uniq) // shall we be restrictive, and check the ethernet address ? && !bcmp(rfc->peer_address, from, ETHER_ADDR_LEN) ) { #endif // bcopy(from, rfc->peer_address, ETHER_ADDR_LEN); rfc->state = PPPOE_STATE_CONNECTED; rfc->session_id = sessid; send_event(rfc, PPPOE_EVT_CONNECTED, 0); return 1; #ifndef PPPENET_COMPAT } #endif return 0; } /* ----------------------------------------------------------------------------- m contains ethernet header and the actual ethernet data from MUST be a valid ethernet address (6 bytes length) ----------------------------------------------------------------------------- */ u_int16_t handle_PADT(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from) { struct pppoe *p = mbuf_data(m); u_int16_t sessid = ntohs(p->sessid); if (rfc->state != PPPOE_STATE_CONNECTED) return 0; if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE receive PADT (0x%x): requested session id = 0x%x, our session id = 0x%x\n", rfc, sessid, rfc->session_id); if ((sessid == rfc->session_id) && !bcmp(rfc->peer_address, from, ETHER_ADDR_LEN)) { rfc->state = PPPOE_STATE_DISCONNECTED; bzero(rfc->peer_address, sizeof(rfc->peer_address)); send_event(rfc, PPPOE_EVT_DISCONNECTED, 0); return 1; } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ u_int16_t handle_ctrl(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from) { struct pppoe *p = mbuf_data(m); u_int16_t done = 0; //log(LOGVAL, "handle_ctrl, rfc = 0x%x\n", rfc); switch (p->code) { case PPPOE_PADI: done = handle_PADI(rfc, m, from); break; case PPPOE_PADO: done = handle_PADO(rfc, m, from); break; case PPPOE_PADR: done = handle_PADR(rfc, m, from); break; case PPPOE_PADS: done = handle_PADS(rfc, m, from); break; case PPPOE_PADT: done = handle_PADT(rfc, m, from); break; } // time to free mbuf now... if (done) mbuf_freem(m); return done; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ u_int16_t handle_data(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *from) { struct pppoe *p = mbuf_data(m); u_int16_t sessid = ntohs(p->sessid); //log(LOGVAL, "handle_data, rfc = 0x%x, from %x:%x:%x:%x:%x:%x\n", rfc, from[0],from[1],from[2],from[3],from[4],from[5] ); //log(LOGVAL, "handle_data, rfc = 0x%x, rfc->peer_address %x:%x:%x:%x:%x:%x\n", rfc, rfc->peer_address[0],rfc->peer_address[1],rfc->peer_address[2],rfc->peer_address[3],rfc->peer_address[4],rfc->peer_address[5] ); //log(LOGVAL, "handle_data, rfc = 0x%x, rfc->state = %d, rfc->session_id = 0x%x, rfc->session_id = 0x%x\n", rfc, rfc->state, rfc->session_id, sessid); // check identify the session, we must check the session id AND the address of the peer // we could be connected to 2 different AC with the same session id // or to 1 AC with 2 session id if ((rfc->state == PPPOE_STATE_CONNECTED) && (rfc->session_id == sessid) && !bcmp(rfc->peer_address, from, ETHER_ADDR_LEN)) { // adjust the packet length // don't only use the m_adj function, because the packet we get here is a etherneet packet // and ethernet packet have a minimum size of 64 bytes. // i.e when getting small packets, mbuf still has at least 64 bytes. // so we need to adjust the len accrding to the len field from the pppoe header // should do something safer, in case first buffer smaller than 6 bytes ??? if (mbuf_next(m)) mbuf_setlen(m, mbuf_len(m) - sizeof(struct pppoe)); else mbuf_setlen(m, ntohs(p->len)); mbuf_setdata(m, mbuf_data(m) + sizeof(struct pppoe), mbuf_len(m)); mbuf_pkthdr_setlen(m, ntohs(p->len)); // packet is passed up to the host if (rfc->inputcb) (*rfc->inputcb)(rfc->host, m); // let's say the packet have been treated return 1; } // the packet was not for us return 0; } /* ----------------------------------------------------------------------------- called from pppoe_rfc when data need to be sent ----------------------------------------------------------------------------- */ void pppoe_rfc_lower_output(struct pppoe_rfc *rfc, mbuf_t m, u_int8_t *to, u_int16_t typ) { //log(LOGVAL, "PPPoE_output\n"); if (rfc->flags & PPPOE_FLAG_LOOPBACK) { mbuf_t m1 = 0; u_int8_t from[ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 }; *(u_int32_t *)&from[0] = rfc->uniqueid; if (mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &m1) != 0) { mbuf_freem(m); return; } if (mbuf_mclget(MBUF_DONTWAIT, MBUF_TYPE_DATA, &m1) != 0) { mbuf_freem(m); mbuf_freem(m1); return; } mbuf_copydata(m, 0, mbuf_pkthdr_len(m), mbuf_data(m1)); mbuf_setlen(m1, mbuf_pkthdr_len(m)); mbuf_pkthdr_setlen(m1, mbuf_pkthdr_len(m)); mbuf_freem(m); pppoe_rfc_lower_input(rfc->ifp, m1, from, typ); return; } pppoe_dlil_output(rfc->ifp, m, to, typ); } /* ----------------------------------------------------------------------------- called from pppoe_dlil when pppoe data are present ----------------------------------------------------------------------------- */ void pppoe_rfc_lower_input(ifnet_t ifp, mbuf_t m, u_int8_t *from, u_int16_t typ) { struct pppoe_rfc *rfc, *lastrfc = 0; //log(LOGVAL, "PPPoE inputdata, tag = %d\n", dl_tag); lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); TAILQ_FOREACH(rfc, &pppoe_rfc_head, next) { // we use dl_tag because we only respond to the peer on the same interface if (rfc->ifp == ifp) { if (pppoe_rfc_input(rfc, m, from, typ)) return; lastrfc = rfc; } } // the last matching rfc itself is irrelevant, just need unit number and tag information log(LOGVAL, "PPPoE inputdata: unexpected %s packet on unit = %d\n", (typ == PPPOE_ETHERTYPE_CTRL ? "control" : "data"), lastrfc ? lastrfc->unit : -1); if (typ == PPPOE_ETHERTYPE_DATA) { // in case of PPPOE_ETHERTYPE_DATA, send a PADT to the peer // trying to talk to us with an incorrect session id struct pppoe *p = mbuf_data(m); u_int16_t sessid = ntohs(p->sessid); if (lastrfc) send_PAD(lastrfc, from, PPPOE_PADT, sessid, 0, 0, 0, 0, 0); } // nobody was intersted in the packet, just ignore it mbuf_freem(m); } /* ----------------------------------------------------------------------------- calls when the lower layer is detaching ----------------------------------------------------------------------------- */ void pppoe_rfc_lower_detaching(ifnet_t ifp) { struct pppoe_rfc *rfc; TAILQ_FOREACH(rfc, &pppoe_rfc_head, next) { if (rfc->ifp == ifp) { if (rfc->flags & PPPOE_FLAG_DEBUG) log(LOGVAL, "PPPoE lower layer detaching (0x%x): ethernet unit = %d\n", rfc, rfc->unit); lck_mtx_unlock(ppp_domain_mutex); pppoe_dlil_detach(rfc->ifp); lck_mtx_lock(ppp_domain_mutex); rfc->ifp = 0; rfc->unit = 0xFFFF; if (rfc->state != PPPOE_STATE_DISCONNECTED) { rfc->state = PPPOE_STATE_DISCONNECTED; bzero(rfc->peer_address, sizeof(rfc->peer_address)); send_event(rfc, PPPOE_EVT_DISCONNECTED, ENXIO); } } } }