/* * 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@ */ /* ----------------------------------------------------------------------------- * * History : * * Feb 2001 - Christophe Allie - created. * * Theory of operation : * * this file implements the link operations for ppp * ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Includes ----------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include "ppp_defs.h" // public ppp values #include "if_ppp.h" // public ppp API #include "if_ppplink.h" // public link API #include "ppp_domain.h" #include "ppp_if.h" /* ----------------------------------------------------------------------------- Definitions ----------------------------------------------------------------------------- */ /* Macro facilities */ #define LKIFNET(lk) (((struct ppp_link *)lk)->lk_ifnet) #define LKNAME(lk) (((struct ppp_link*)lk)->lk_name) #define LKUNIT(lk) (((struct ppp_link*)lk)->lk_unit) #define LKIFFDEBUG(lk) (LKIFNET(lk) ? ifnet_flags(LKIFNET(lk)) & IFF_DEBUG : 0 ) #define LKIFNAME(lk) (LKIFNET(lk) ? ifnet_name(LKIFNET(lk)) : "???") #define LKIFUNIT(lk) (LKIFNET(lk) ? ifnet_unit(LKIFNET(lk)) : 0) #define LOGLKDBG(lk, text) \ if (LKIFNET(lk) && (ifnet_flags(LKIFNET(lk)) & IFF_DEBUG)) { \ log text; \ } /* As the private structure contains only one field, there is no need for an extra malloc . Can be changed later, if we need to keep more information in the private structure. */ #ifdef USE_PRIVATE_STRUCT /* Link private data structure */ struct ppp_priv { void *host; /* our client structure */ }; #endif /* ----------------------------------------------------------------------------- Forward declarations ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Globals ----------------------------------------------------------------------------- */ static TAILQ_HEAD(, ppp_link) ppp_link_head; extern lck_mtx_t *ppp_domain_mutex; /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_link_init() { TAILQ_INIT(&ppp_link_head); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_link_dispose() { // can't dispose if serial lines are in use if (TAILQ_FIRST(&ppp_link_head)) return EBUSY; return 0; } /* ----------------------------------------------------------------------------- find a free unit in the interface list ----------------------------------------------------------------------------- */ u_short ppp_link_findfreeindex() { struct ppp_link *link = TAILQ_FIRST(&ppp_link_head); u_short index = 0; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); while (link) { if (link->lk_index == index) { index++; link = TAILQ_FIRST(&ppp_link_head); // restart } else link = TAILQ_NEXT(link, lk_next); // continue } return index; } /* ----------------------------------------------------------------------------- Attach a link ----------------------------------------------------------------------------- */ int ppp_link_attach(struct ppp_link *link) { #ifdef USE_PRIVATE_STRUCT struct ppp_priv *priv; #endif lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (!link->lk_ioctl || !link->lk_output) { return EINVAL; } #ifdef USE_PRIVATE_STRUCT MALLOC(priv, struct ppp_priv *, sizeof(struct ppp_priv), M_TEMP, M_WAITOK); if (!priv) return ENOMEM; bzero(priv, sizeof(struct ppp_priv)); link->lk_ppp_private = priv; #else link->lk_ppp_private = 0; #endif link->lk_ifnet = 0; link->lk_index = ppp_link_findfreeindex(); TAILQ_INSERT_TAIL(&ppp_link_head, link, lk_next); return 0; } /* ----------------------------------------------------------------------------- Detach a link ----------------------------------------------------------------------------- */ int ppp_link_detach(struct ppp_link *link) { #ifdef USE_PRIVATE_STRUCT struct ppp_priv *priv = (struct ppp_priv *)link->lk_ppp_private; #endif lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); LOGLKDBG(link, (LOGVAL, "ppp_link_detach : (link = %s%d)\n", LKNAME(link), LKUNIT(link))); ppp_if_detachlink(link); #ifdef USE_PRIVATE_STRUCT ppp_proto_free(priv->host); FREE(priv, M_TEMP); #else ppp_proto_free(link->lk_ppp_private); #endif TAILQ_REMOVE(&ppp_link_head, link, lk_next); link->lk_ppp_private = 0; return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_link_event(struct ppp_link *link, u_int32_t event, void *data) { lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); switch (event) { case PPP_LINK_EVT_XMIT_OK: if (link->lk_ifnet) ppp_if_xmit(link->lk_ifnet, 0); break; case PPP_LINK_EVT_INPUTERROR: if (link->lk_ifnet) ppp_if_error(link->lk_ifnet); break; } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_link_input(struct ppp_link *link, mbuf_t m) { #ifdef USE_PRIVATE_STRUCT struct ppp_priv *priv = (struct ppp_priv *)link->lk_ppp_private; #endif u_char *p; u_int16_t proto, len; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (link->lk_ifnet && (ifnet_flags(link->lk_ifnet) & PPP_LOG_INPKT)) ppp_link_logmbuf(link, "ppp_link_input", m); p = mbuf_data(m); if ((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI)) { mbuf_adj(m, 2); p = mbuf_data(m); } proto = p[0]; len = 1; if (!(proto & 0x1)) { // lowest bit set for lowest byte of protocol proto = (proto << 8) + p[1]; len = 2; } if (link->lk_ifnet && (proto < 0xC000)) { ppp_if_input(link->lk_ifnet, m, proto, len); // Network protocol } else { #ifdef USE_PRIVATE_STRUCT ppp_proto_input(priv->host, m); // LCP/Auth/unexpected network protocol #else ppp_proto_input(link->lk_ppp_private, m);// LCP/Auth/unexpected network protocol #endif } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_link_control(struct ppp_link *link, u_int32_t cmd, void *data) { int error = 0; u_int32_t flags, mru; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); switch (cmd) { case PPPIOCCONNECT: LOGLKDBG(link, (LOGVAL, "ppp_link_control : PPPIOCCONNECT, (link = %s%d), attach to interface = %d \n", LKNAME(link), LKUNIT(link), *(u_int32_t *)data)); error = ppp_if_attachlink(link, *(u_int32_t *)data); break; case PPPIOCDISCONN: LOGLKDBG(link, (LOGVAL, "ppp_link_control : PPPIOCDISCONN, (link = %s%d), detach from interface = %s%d \n", LKNAME(link), LKUNIT(link), LKIFNAME(link), LKIFUNIT(link))); error = ppp_if_detachlink(link); break; default: error = ENOTSUP; } if (error != ENOTSUP) return error; error = (*link->lk_ioctl)(link, cmd, data); if (error != ENOTSUP) return error; error = 0; switch (cmd) { case PPPIOCGFLAGS: LOGLKDBG(link, (LOGVAL, "ppp_link_control: (link = %s%d), PPPIOCGFLAGS = 0x%x\n", LKNAME(link), LKUNIT(link), link->lk_flags)); *(u_int32_t *)data = link->lk_flags; break; case PPPIOCSFLAGS: LOGLKDBG(link, (LOGVAL, "ppp_link_control: (link = %s%d), PPPIOCSFLAGS = 0x%x\n", LKNAME(link), LKUNIT(link), *(u_int32_t *)data & SC_MASK)); flags = *(u_int32_t *)data & SC_MASK; link->lk_flags = (link->lk_flags & ~SC_MASK) | flags; break; case PPPIOCGMRU: LOGLKDBG(link, (LOGVAL, "ppp_link_control: (link = %s%d), PPPIOCGMRU = 0x%x\n", LKNAME(link), LKUNIT(link), link->lk_mru)); *(u_int32_t *)data = link->lk_mru; break; case PPPIOCSMRU: LOGLKDBG(link, (LOGVAL, "ppp_link_control: (link = %s%d), PPPIOCSMRU = 0x%x\n", LKNAME(link), LKUNIT(link), *(u_int32_t *)data)); mru = *(u_int32_t *)data; link->lk_mru = mru; break; case PPPIOCSASYNCMAP: case PPPIOCSRASYNCMAP: case PPPIOCSXASYNCMAP: case PPPIOCGASYNCMAP: case PPPIOCGRASYNCMAP: case PPPIOCGXASYNCMAP: if ((link->lk_support & PPP_LINK_ASYNC) == 0) error = EINVAL; // async link must support these ioctls break; default: error = EINVAL; } return error; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_link_attachclient(u_short index, void *host, struct ppp_link **data) { struct ppp_link *link; #ifdef USE_PRIVATE_STRUCT struct ppp_priv *priv; #endif lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); TAILQ_FOREACH(link, &ppp_link_head, lk_next) { if (link->lk_index == index) { *data = (void *)link; #ifdef USE_PRIVATE_STRUCT priv = (struct ppp_priv *)link->lk_ppp_private; if (priv->host) return EBUSY; /* a client is already attached */ priv->host = host; #else if (link->lk_ppp_private) return EBUSY; /* a client is already attached */ link->lk_ppp_private = host; #endif return 0; } } return ENODEV; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_link_detachclient(struct ppp_link *link, void *host) { #ifdef USE_PRIVATE_STRUCT struct ppp_priv *priv = (struct ppp_priv *)link->lk_ppp_private; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (priv && (priv->host == host)) priv->host = 0; #else if (link->lk_ppp_private == host) link->lk_ppp_private = 0; #endif } /* ----------------------------------------------------------------------------- we wend packet without link framing (FF03) it's the reponsability of the driver to add the header, it the links need it. it should be done accordingly to the ppp negociation as well. ----------------------------------------------------------------------------- */ int ppp_link_send(struct ppp_link *link, mbuf_t m) { u_char *p = mbuf_data(m); u_int16_t proto = ((u_int16_t)p[0] << 8) + p[1]; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); // if pcomp has been negociated, remove leading 0 byte if ((link->lk_flags & SC_COMP_PROT) && !p[0]) { mbuf_adj(m, 1); } // if accomp has not been negociated, or if the needs FF03, add the header if (!(link->lk_support & PPP_LINK_DEL_AC)) { if ((proto == PPP_LCP) || !(link->lk_flags & SC_COMP_AC)) { if (mbuf_prepend(&m, 2, MBUF_DONTWAIT) != 0) return ENOBUFS; p = mbuf_data(m); p[0] = PPP_ALLSTATIONS; p[1] = PPP_UI; } } if (link->lk_ifnet && (ifnet_flags(link->lk_ifnet) & PPP_LOG_OUTPKT)) ppp_link_logmbuf(link, "ppp_link_send", m); /* link level packet are send oot of band */ if ((proto >= 0xC000) && (link->lk_support & PPP_LINK_OOB_QUEUE)) mbuf_settype(m, MBUF_TYPE_OOBDATA); return (*link->lk_output)(link, m); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_link_logmbuf(struct ppp_link *link, char *msg, mbuf_t m) { int i, lcount, copycount, count; char lbuf[16], *data; if (m == NULL) return; log(LOGVAL, "%s: [ifnet = %s%d] [link = %s%d]\n", msg, LKIFNAME(link), LKIFUNIT(link), LKNAME(link), LKUNIT(link)); for (count = mbuf_len(m), data = mbuf_data(m); m != NULL; ) { /* build a line of output */ for(lcount = 0; lcount < sizeof(lbuf); lcount += copycount) { if (!count) { m = mbuf_next(m); if (m == NULL) break; count = mbuf_len(m); data = mbuf_data(m); } copycount = (count > sizeof(lbuf) - lcount) ? sizeof(lbuf) - lcount : count; bcopy(data, &lbuf[lcount], copycount); data += copycount; count -= copycount; } /* output line (hex 1st, then ascii) */ log(LOGVAL, "%s: 0x ", msg); for(i = 0; i < lcount; i++) { if (i == 8) log(LOGVAL, " "); log(LOGVAL, "%02x ", (u_char)lbuf[i]); } for( ; i < sizeof(lbuf); i++) { if (i == 8) log(LOGVAL, " "); log(LOGVAL, " "); } log(LOGVAL, " '"); for(i = 0; i < lcount; i++) log(LOGVAL, "%c",(lbuf[i]>=040 && lbuf[i]<=0176)?lbuf[i]:'.'); log(LOGVAL, "'\n"); } }