1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/* -----------------------------------------------------------------------------
25*
26*  History :
27*
28*  Feb 2001 - 	Christophe Allie - created.
29*
30*  Theory of operation :
31*
32*  this file implements the link operations for ppp
33*
34----------------------------------------------------------------------------- */
35
36
37
38/* -----------------------------------------------------------------------------
39Includes
40----------------------------------------------------------------------------- */
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/mbuf.h>
45#include <sys/socket.h>
46#include <sys/malloc.h>
47#include <sys/syslog.h>
48#include <sys/sockio.h>
49#include <kern/locks.h>
50#include <net/if_types.h>
51#include <net/if.h>
52#include <netinet/in.h>
53
54#include "ppp_defs.h"		// public ppp values
55#include "if_ppp.h"		// public ppp API
56#include "if_ppplink.h"		// public link API
57#include "ppp_domain.h"
58#include "ppp_if.h"
59
60/* -----------------------------------------------------------------------------
61Definitions
62----------------------------------------------------------------------------- */
63
64/* Macro facilities */
65
66#define LKIFNET(lk)		(((struct ppp_link *)lk)->lk_ifnet)
67#define LKNAME(lk) 		(((struct ppp_link*)lk)->lk_name)
68#define LKUNIT(lk) 		(((struct ppp_link*)lk)->lk_unit)
69
70#define LKIFFDEBUG(lk) 		(LKIFNET(lk) ? ifnet_flags(LKIFNET(lk)) & IFF_DEBUG : 0 )
71#define LKIFNAME(lk) 		(LKIFNET(lk) ? ifnet_name(LKIFNET(lk)) : "???")
72#define LKIFUNIT(lk) 		(LKIFNET(lk) ? ifnet_unit(LKIFNET(lk)) : 0)
73
74#define LOGLKDBG(lk, text) \
75    if (LKIFNET(lk) && (ifnet_flags(LKIFNET(lk)) & IFF_DEBUG)) {	\
76        IOLog text; 		\
77    }
78
79/*
80    As the private structure contains only one field,
81    there is no need for an extra malloc .
82    Can be changed later, if we need to keep
83    more information in the private structure.
84*/
85#ifdef USE_PRIVATE_STRUCT
86/* Link private data structure */
87struct ppp_priv {
88    void 		*host;		/* our client structure */
89};
90#endif
91
92/* -----------------------------------------------------------------------------
93Forward declarations
94----------------------------------------------------------------------------- */
95
96
97
98/* -----------------------------------------------------------------------------
99Globals
100----------------------------------------------------------------------------- */
101
102static TAILQ_HEAD(, ppp_link) 	ppp_link_head;
103extern lck_mtx_t   *ppp_domain_mutex;
104
105/* -----------------------------------------------------------------------------
106----------------------------------------------------------------------------- */
107int ppp_link_init()
108{
109    TAILQ_INIT(&ppp_link_head);
110
111    return 0;
112}
113
114/* -----------------------------------------------------------------------------
115----------------------------------------------------------------------------- */
116int ppp_link_dispose()
117{
118     // can't dispose if serial lines are in use
119    if (TAILQ_FIRST(&ppp_link_head))
120        return EBUSY;
121
122    return 0;
123}
124
125/* -----------------------------------------------------------------------------
126find a free unit in the interface list
127----------------------------------------------------------------------------- */
128u_short ppp_link_findfreeindex()
129{
130    struct ppp_link  	*link = TAILQ_FIRST(&ppp_link_head);
131    u_short 		index = 0;
132
133	lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
134
135    while (link) {
136    	if (link->lk_index == index) {
137            index++;
138            link = TAILQ_FIRST(&ppp_link_head); // restart
139        }
140        else
141            link = TAILQ_NEXT(link, lk_next); // continue
142    }
143    return index;
144}
145
146/* -----------------------------------------------------------------------------
147Attach a link
148----------------------------------------------------------------------------- */
149int ppp_link_attach(struct ppp_link *link)
150{
151#ifdef USE_PRIVATE_STRUCT
152    struct ppp_priv *priv;
153#endif
154
155	lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
156
157    if (!link->lk_ioctl || !link->lk_output) {
158        return EINVAL;
159    }
160
161#ifdef USE_PRIVATE_STRUCT
162    MALLOC(priv, struct ppp_priv *, sizeof(struct ppp_priv), M_TEMP, M_WAITOK);
163    if (!priv)
164        return ENOMEM;
165
166    bzero(priv, sizeof(struct ppp_priv));
167    link->lk_ppp_private = priv;
168#else
169    link->lk_ppp_private = 0;
170#endif
171    link->lk_ifnet = 0;
172    link->lk_index = ppp_link_findfreeindex();
173    TAILQ_INSERT_TAIL(&ppp_link_head, link, lk_next);
174
175    return 0;
176}
177
178/* -----------------------------------------------------------------------------
179Detach a link
180----------------------------------------------------------------------------- */
181int ppp_link_detach(struct ppp_link *link)
182{
183#ifdef USE_PRIVATE_STRUCT
184    struct ppp_priv 	*priv = (struct ppp_priv *)link->lk_ppp_private;
185#endif
186
187	lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
188
189    LOGLKDBG(link, ("ppp_link_detach : (link = %s%d)\n", LKNAME(link), LKUNIT(link)));
190    ppp_if_detachlink(link);
191#ifdef USE_PRIVATE_STRUCT
192    ppp_proto_free(priv->host);
193    FREE(priv, M_TEMP);
194#else
195    ppp_proto_free(link->lk_ppp_private);
196#endif
197    TAILQ_REMOVE(&ppp_link_head, link, lk_next);
198    link->lk_ppp_private = 0;
199    return 0;
200}
201
202/* -----------------------------------------------------------------------------
203----------------------------------------------------------------------------- */
204int ppp_link_event(struct ppp_link *link, u_int32_t event, void *data)
205{
206
207	lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
208
209    switch (event) {
210        case PPP_LINK_EVT_XMIT_OK:
211            if (link->lk_ifnet)
212                ppp_if_xmit(link->lk_ifnet, 0);
213            break;
214        case PPP_LINK_EVT_INPUTERROR:
215            if (link->lk_ifnet)
216                ppp_if_error(link->lk_ifnet);
217            break;
218    }
219    return 0;
220}
221
222/* -----------------------------------------------------------------------------
223----------------------------------------------------------------------------- */
224int ppp_link_input(struct ppp_link *link, mbuf_t m)
225{
226#ifdef USE_PRIVATE_STRUCT
227    struct ppp_priv 	*priv = (struct ppp_priv *)link->lk_ppp_private;
228#endif
229    u_char 		*p;
230    u_int16_t		proto, len;
231
232	lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
233
234    if (link->lk_ifnet && (ifnet_flags(link->lk_ifnet) & PPP_LOG_INPKT))
235        ppp_link_logmbuf(link, "ppp_link_input", m);
236
237	if (mbuf_len(m) < PPP_HDRLEN &&
238		mbuf_pullup(&m, PPP_HDRLEN)) {
239			if (m) {
240				mbuf_freem(m);
241				m = NULL;
242			}
243			IOLog("ppp_link_input: cannot pullup header\n");
244			return 0;
245	}
246
247    p = mbuf_data(m);	// no alignment issue as p is *uchar.
248    if ((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI)) {
249        mbuf_adj(m, 2);
250        p = mbuf_data(m);
251    }
252    proto = p[0];
253    len = 1;
254    if (!(proto & 0x1)) {  // lowest bit set for lowest byte of protocol
255        proto = (proto << 8) + p[1];
256        len = 2;
257    }
258
259    if (link->lk_ifnet && (proto < 0xC000)) {
260        ppp_if_input(link->lk_ifnet, m, proto, len);	// Network protocol
261    }
262    else {
263#ifdef USE_PRIVATE_STRUCT
264	ppp_proto_input(priv->host, m);		// LCP/Auth/unexpected network protocol
265#else
266        ppp_proto_input(link->lk_ppp_private, m);// LCP/Auth/unexpected network protocol
267#endif
268    }
269    return 0;
270}
271
272/* -----------------------------------------------------------------------------
273----------------------------------------------------------------------------- */
274int ppp_link_control(struct ppp_link *link, u_long cmd, void *data)
275{
276    int 	error = 0;
277    u_int32_t	flags, mru;
278
279	lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
280
281    switch (cmd) {
282        case PPPIOCCONNECT:
283            LOGLKDBG(link, ("ppp_link_control : PPPIOCCONNECT, (link = %s%d), attach to interface = %d \n",
284                LKNAME(link), LKUNIT(link), *(u_int32_t *)data));
285            error = ppp_if_attachlink(link, *(u_int32_t *)data);
286            break;
287        case PPPIOCDISCONN:
288            LOGLKDBG(link, ("ppp_link_control : PPPIOCDISCONN, (link = %s%d), detach from interface = %s%d \n",
289                LKNAME(link), LKUNIT(link), LKIFNAME(link), LKIFUNIT(link)));
290            error = ppp_if_detachlink(link);
291            break;
292        default:
293            error = ENOTSUP;
294    }
295    if (error != ENOTSUP)
296        return error;
297
298    error = (*link->lk_ioctl)(link, cmd, data);
299    if (error != ENOTSUP)
300        return error;
301
302    error = 0;
303    switch (cmd) {
304
305        case PPPIOCGFLAGS:
306            LOGLKDBG(link, ("ppp_link_control:  (link = %s%d), PPPIOCGFLAGS = 0x%x\n",
307                LKNAME(link), LKUNIT(link), link->lk_flags));
308            *(u_int32_t *)data = link->lk_flags;
309            break;
310
311        case PPPIOCSFLAGS:
312            LOGLKDBG(link, ("ppp_link_control:  (link = %s%d), PPPIOCSFLAGS = 0x%x\n",
313                LKNAME(link), LKUNIT(link), *(u_int32_t *)data & SC_MASK));
314            flags = *(u_int32_t *)data & SC_MASK;
315            link->lk_flags = (link->lk_flags & ~SC_MASK) | flags;
316            break;
317
318        case PPPIOCGMRU:
319            LOGLKDBG(link, ("ppp_link_control:  (link = %s%d), PPPIOCGMRU = 0x%x\n",
320                LKNAME(link), LKUNIT(link), link->lk_mru));
321            *(u_int32_t *)data = link->lk_mru;
322            break;
323
324        case PPPIOCSMRU:
325            LOGLKDBG(link, ("ppp_link_control:  (link = %s%d), PPPIOCSMRU = 0x%x\n",
326                LKNAME(link), LKUNIT(link), *(u_int32_t *)data));
327            mru = *(u_int32_t *)data;
328            link->lk_mru = mru;
329            break;
330
331
332        case PPPIOCSASYNCMAP:
333        case PPPIOCSRASYNCMAP:
334        case PPPIOCSXASYNCMAP:
335        case PPPIOCGASYNCMAP:
336        case PPPIOCGRASYNCMAP:
337        case PPPIOCGXASYNCMAP:
338            if ((link->lk_support & PPP_LINK_ASYNC) == 0)
339                error = EINVAL; 	// async link must support these ioctls
340            break;
341
342
343        default:
344            error = EINVAL;
345    }
346    return error;
347}
348
349/* -----------------------------------------------------------------------------
350----------------------------------------------------------------------------- */
351int ppp_link_attachclient(u_short index, void *host, struct ppp_link **data)
352{
353    struct ppp_link  	*link;
354#ifdef USE_PRIVATE_STRUCT
355    struct ppp_priv	*priv;
356#endif
357
358	lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
359
360    TAILQ_FOREACH(link, &ppp_link_head, lk_next) {
361        if (link->lk_index == index) {
362            *data = (void *)link;
363#ifdef USE_PRIVATE_STRUCT
364            priv = (struct ppp_priv *)link->lk_ppp_private;
365			if (priv->host)
366				return EBUSY;	/* a client is already attached */
367            priv->host = host;
368#else
369			if (link->lk_ppp_private)
370				return EBUSY;	/* a client is already attached */
371            link->lk_ppp_private = host;
372#endif
373            return 0;
374        }
375    }
376    return ENODEV;
377}
378
379/* -----------------------------------------------------------------------------
380----------------------------------------------------------------------------- */
381void ppp_link_detachclient(struct ppp_link *link, void *host)
382{
383#ifdef USE_PRIVATE_STRUCT
384    struct ppp_priv	*priv = (struct ppp_priv *)link->lk_ppp_private;
385
386	lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
387
388    if (priv && (priv->host == host))
389        priv->host = 0;
390#else
391    if (link->lk_ppp_private == host)
392        link->lk_ppp_private = 0;
393#endif
394}
395
396/* -----------------------------------------------------------------------------
397we wend packet without link framing (FF03)
398it's the reponsability of the driver to add the header, it the links need it.
399it should be done accordingly to the ppp negociation as well.
400----------------------------------------------------------------------------- */
401int ppp_link_send(struct ppp_link *link, mbuf_t m)
402{
403    u_char 	*p = mbuf_data(m);	// no alignment issue as p is *uchar.
404    u_int16_t 	proto = ((u_int16_t)p[0] << 8) + p[1];
405
406	lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED);
407
408    // if pcomp has been negociated, remove leading 0 byte
409    if ((link->lk_flags & SC_COMP_PROT) && !p[0]) {
410	mbuf_adj(m, 1);
411    }
412
413    // if accomp has not been negociated, or if the needs FF03, add the header
414    if (!(link->lk_support & PPP_LINK_DEL_AC)) {
415        if ((proto == PPP_LCP)
416            || !(link->lk_flags & SC_COMP_AC)) {
417
418        if (mbuf_prepend(&m, 2, MBUF_DONTWAIT) != 0)
419            return ENOBUFS;
420
421        p = mbuf_data(m);
422        p[0] = PPP_ALLSTATIONS;
423        p[1] = PPP_UI;
424      }
425    }
426
427    if (link->lk_ifnet && (ifnet_flags(link->lk_ifnet) & PPP_LOG_OUTPKT))
428        ppp_link_logmbuf(link, "ppp_link_send", m);
429
430	/* link level packet are send oot of band */
431    if ((proto >= 0xC000) &&
432		(link->lk_support & PPP_LINK_OOB_QUEUE))
433		mbuf_settype(m, MBUF_TYPE_OOBDATA);
434
435    return (*link->lk_output)(link, m);
436}
437
438/* -----------------------------------------------------------------------------
439----------------------------------------------------------------------------- */
440void ppp_link_logmbuf(struct ppp_link *link, char *msg, mbuf_t m)
441{
442    int 	i, lcount, copycount, count;
443    char 	lbuf[16], *data;
444
445    if (m == NULL)
446        return;
447
448    IOLog("%s: [ifnet = %s%d] [link = %s%d]\n", msg,
449            LKIFNAME(link), LKIFUNIT(link), LKNAME(link), LKUNIT(link));
450
451    for (count = mbuf_len(m), data = mbuf_data(m); m != NULL; ) {	// no alignment issue as data is *uchar.
452        /* build a line of output */
453        for(lcount = 0; lcount < sizeof(lbuf); lcount += copycount) {
454            if (!count) {
455                m = mbuf_next(m);
456                if (m == NULL)
457                    break;
458                count = mbuf_len(m);
459                data  = mbuf_data(m);
460            }
461            copycount = (count > sizeof(lbuf) - lcount) ? sizeof(lbuf) - lcount : count;
462            bcopy(data, &lbuf[lcount], copycount);
463            data  += copycount;
464            count -= copycount;
465        }
466
467        /* output line (hex 1st, then ascii) */
468        IOLog("%s:  0x  ", msg);
469        for(i = 0; i < lcount; i++) {
470            if (i == 8) IOLog("  ");
471            IOLog("%02x ", (u_char)lbuf[i]);
472        }
473        for( ; i < sizeof(lbuf); i++) {
474            if (i == 8) IOLog("  ");
475            IOLog("   ");
476        }
477        IOLog("  '");
478        for(i = 0; i < lcount; i++)
479            IOLog("%c",(lbuf[i]>=040 && lbuf[i]<=0176)?lbuf[i]:'.');
480        IOLog("'\n");
481    }
482}
483
484