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 *  Theory of operation :
27 *
28 *  This file implements the ip protocol module for the ppp interface
29 *
30----------------------------------------------------------------------------- */
31
32/* -----------------------------------------------------------------------------
33Includes
34----------------------------------------------------------------------------- */
35
36#include <sys/param.h>
37#include <sys/mbuf.h>
38#include <sys/socket.h>
39#include <sys/syslog.h>
40#include <sys/systm.h>
41#include <sys/malloc.h>
42#include <sys/sockio.h>
43#include <kern/locks.h>
44
45#include <net/if.h>
46#include <net/kpi_protocol.h>
47
48#include <netinet/in.h>
49#include <netinet/in_systm.h>
50#include <netinet/in_var.h>
51#include <netinet/ip.h>
52#include <netinet/udp.h>
53#include <netinet/bootp.h>
54
55#include "ppp_defs.h"		// public ppp values
56#include "ppp_ip.h"
57#include "ppp_domain.h"
58#include "ppp_if.h"
59#include "if_ppplink.h"
60
61
62/* -----------------------------------------------------------------------------
63Definitions
64----------------------------------------------------------------------------- */
65
66/* -----------------------------------------------------------------------------
67Forward declarations
68----------------------------------------------------------------------------- */
69
70static errno_t ppp_ip_input(ifnet_t ifp, protocol_family_t protocol,
71									 mbuf_t packet, char* header);
72static errno_t ppp_ip_preoutput(ifnet_t ifp, protocol_family_t protocol,
73									mbuf_t *packet, const struct sockaddr *dest,
74									void *route, char *frame_type, char *link_layer_dest);
75static errno_t ppp_ip_ioctl(ifnet_t ifp, protocol_family_t protocol,
76									 u_long command, void* argument);
77
78/* -----------------------------------------------------------------------------
79Globals
80----------------------------------------------------------------------------- */
81extern lck_mtx_t   *ppp_domain_mutex;
82
83/* -----------------------------------------------------------------------------
84init function
85----------------------------------------------------------------------------- */
86int ppp_ip_init(int init_arg)
87{
88    return proto_register_plumber(PF_INET, APPLE_IF_FAM_PPP,
89							   ppp_ip_attach, ppp_ip_detach);
90}
91
92/* -----------------------------------------------------------------------------
93terminate function
94----------------------------------------------------------------------------- */
95int ppp_ip_dispose(int term_arg)
96{
97    proto_unregister_plumber(PF_INET, APPLE_IF_FAM_PPP);
98    return 0;
99}
100
101/* -----------------------------------------------------------------------------
102attach the PPPx interface ifp to the network protocol IP,
103called when the ppp interface is ready for ppp traffic
104----------------------------------------------------------------------------- */
105errno_t ppp_ip_attach(ifnet_t ifp, protocol_family_t protocol)
106{
107    int					ret;
108    struct ifnet_attach_proto_param   reg;
109    struct ppp_if		*wan = (struct ppp_if *)ifnet_softc(ifp);
110
111    LOGDBG(ifp, ("ppp_ip_attach: name = %s, unit = %d\n", ifnet_name(ifp), ifnet_unit(ifp)));
112
113    if (wan->ip_attached)
114        return 0;	// already attached
115
116    bzero(&reg, sizeof(struct ifnet_attach_proto_param));
117
118	reg.input = ppp_ip_input;
119	reg.pre_output = ppp_ip_preoutput;
120	reg.ioctl = ppp_ip_ioctl;
121	ret = ifnet_attach_protocol(ifp, PF_INET, &reg);
122    LOGRETURN(ret, ret, "ppp_ip_attach: ifnet_attach_protocol error = 0x%x\n");
123
124    LOGDBG(ifp, ("ppp_i6_attach: ifnet_attach_protocol family = 0x%x\n", protocol));
125	ifnet_find_by_name("lo0", &wan->lo_ifp);
126	wan->ip_attached = 1;
127
128    return 0;
129}
130
131/* -----------------------------------------------------------------------------
132detach the PPPx interface ifp from the network protocol IP,
133called when the ppp interface stops ip traffic
134----------------------------------------------------------------------------- */
135void ppp_ip_detach(ifnet_t ifp, protocol_family_t protocol)
136{
137    int 		ret;
138    struct ppp_if		*wan = (struct ppp_if *)ifnet_softc(ifp);
139
140    LOGDBG(ifp, ("ppp_ip_detach\n"));
141
142    if (!wan->ip_attached)
143        return;	// already detached
144
145	ifnet_release(wan->lo_ifp);
146	wan->lo_ifp = 0;
147
148    ret = ifnet_detach_protocol(ifp, PF_INET);
149	if (ret)
150        IOLog("ppp_ip_detach: ifnet_detach_protocol error = 0x%x\n", ret);
151
152    wan->ip_attached = 0;
153}
154
155/* -----------------------------------------------------------------------------
156called from dlil when an ioctl is sent to the interface
157----------------------------------------------------------------------------- */
158errno_t ppp_ip_ioctl(ifnet_t ifp, protocol_family_t protocol,
159									 u_long command, void* argument)
160{
161    struct ppp_if		*wan = (struct ppp_if *)ifnet_softc(ifp);
162    struct sockaddr_in  addr, dstaddr;
163    int 		error = 0;
164
165    switch (command) {
166
167        case SIOCSIFADDR:
168        case SIOCAIFADDR:
169            LOGDBG(ifp, ("ppp_ip_ioctl: cmd = SIOCSIFADDR/SIOCAIFADDR\n"));
170
171	    error = ifaddr_address(argument, (struct sockaddr *)&addr, sizeof (addr));
172	    if (error != 0) {
173                error = EAFNOSUPPORT;
174                break;
175	    }
176
177            // only an IPv4 address should arrive here
178            if (addr.sin_family != AF_INET) {
179                error = EAFNOSUPPORT;
180                break;
181            }
182
183	    error = ifaddr_dstaddress(argument, (struct sockaddr *)&dstaddr, sizeof (dstaddr));
184	    if (error != 0) {
185                error = EAFNOSUPPORT;
186                break;
187	    }
188
189            // only an IPv4 address should arrive here
190            if (dstaddr.sin_family != AF_INET) {
191                error = EAFNOSUPPORT;
192                break;
193            }
194
195            wan->ip_src.s_addr = addr.sin_addr.s_addr;
196            wan->ip_dst.s_addr = dstaddr.sin_addr.s_addr;
197            break;
198
199        default :
200            error = EOPNOTSUPP;
201    }
202
203    return error;
204}
205
206/* -----------------------------------------------------------------------------
207called from dlil when a packet from the interface is to be dispatched to
208the specific network protocol attached by dl_tag.
209the network protocol has been determined earlier by the demux function.
210the packet is in the mbuf chain m without
211the frame header, which is provided separately. (not used)
212----------------------------------------------------------------------------- */
213errno_t ppp_ip_input(ifnet_t ifp, protocol_family_t protocol,
214									 mbuf_t packet, char* header)
215{
216
217    LOGMBUF("ppp_ip_input", packet);
218
219	if (proto_input(PF_INET, packet))
220		mbuf_freem(packet);
221
222    return 0;
223}
224
225/* -----------------------------------------------------------------------------
226pre_output function
227----------------------------------------------------------------------------- */
228errno_t ppp_ip_preoutput(ifnet_t ifp, protocol_family_t protocol,
229									mbuf_t *packet, const struct sockaddr *dest,
230									void *route, char *frame_type, char *link_layer_dest)
231{
232    errno_t				err;
233    struct ppp_if		*wan = (struct ppp_if *)ifnet_softc(ifp);
234    u_int16_t           ftype = PPP_IP;
235
236    LOGMBUF("ppp_ip_preoutput", *packet);
237
238	lck_mtx_lock(ppp_domain_mutex);
239
240#if 0
241    (*packet)->m_flags &= ~M_HIGHPRI;
242
243    /* If this packet has the "low delay" bit set in the IP header,
244     set priority bit for the packet. */
245    ip = mtod(*packet, struct ip *);
246    if (ip->ip_tos & IPTOS_LOWDELAY)
247        (*packet)->m_flags |= M_HIGHPRI;
248#endif
249
250    if ((wan->sc_flags & SC_LOOP_LOCAL)
251        && (!memcmp(&((struct sockaddr_in *)(void*)dest)->sin_addr.s_addr, &wan->ip_src.s_addr, sizeof(struct in_addr)))    // Wcast-align fix - memcmp for unaligned compare
252		&& wan->lo_ifp) {
253        err = ifnet_output(wan->lo_ifp, PF_INET, *packet, 0, (struct sockaddr *)dest);
254		lck_mtx_unlock(ppp_domain_mutex);
255        return (err ? err : EJUSTRETURN);
256    }
257	lck_mtx_unlock(ppp_domain_mutex);
258    memcpy(frame_type, &ftype, sizeof(u_int16_t));     // Wcast-align fix - memcpy for unaligned move
259    return 0;
260}
261
262/* -----------------------------------------------------------------------------
263Compare the source address of the packet with the source address of the interface
264----------------------------------------------------------------------------- */
265int ppp_ip_af_src_out(ifnet_t ifp, char *pkt)
266{
267    struct ppp_if	*wan = (struct ppp_if *)ifnet_softc(ifp);
268    struct ip 		*ip;
269
270    // Wcast-align fixes - use memcmp for unaligned move
271    ip = (struct ip *)(void*)pkt;
272    return (memcmp(&ip->ip_src.s_addr, &wan->ip_src.s_addr, sizeof(struct in_addr)) == 0 ? 0 : 1);
273}
274
275/* -----------------------------------------------------------------------------
276Compare the source address of the packet with the dst address of the interface
277----------------------------------------------------------------------------- */
278int ppp_ip_af_src_in(ifnet_t ifp, char *pkt)
279{
280    struct ppp_if	*wan = (struct ppp_if *)ifnet_softc(ifp);
281    struct ip 		*ip;
282
283    // Wcast-align fixes - use memcmp for unaligned accesses
284    ip = (struct ip *)(void*)pkt;
285    return (memcmp(&ip->ip_src.s_addr, &wan->ip_dst.s_addr, sizeof(struct in_addr)) == 0 ? 0 : 1);
286}
287
288/* -----------------------------------------------------------------------------
289Check if the packet is a bootp packet for us
290----------------------------------------------------------------------------- */
291int ppp_ip_bootp_client_in(ifnet_t ifp, char *pkt)
292{
293    struct ppp_if	*wan = (struct ppp_if *)ifnet_softc(ifp);
294    struct ip 		*ip;
295	struct udphdr	udp;
296
297    // Wcast-align fixes - use memcmp and memcpy for unaligned accesses
298    ip = (struct ip *)(void*)pkt;
299    if (!memcmp(&ip->ip_dst.s_addr, &wan->ip_src.s_addr, sizeof(struct in_addr)) && ip->ip_p == IPPROTO_UDP) {
300
301		memcpy(&udp, pkt + sizeof(struct ip), sizeof(struct udphdr));
302		if (udp.uh_sport == htons(IPPORT_BOOTPS) && udp.uh_dport == htons(IPPORT_BOOTPC)) {
303				return 1;
304		}
305	}
306
307	return 0;
308}
309
310/* -----------------------------------------------------------------------------
311Check if the packet is a broadcast bootp packet
312----------------------------------------------------------------------------- */
313int ppp_ip_bootp_server_in(ifnet_t ifp, char *pkt)
314{
315    //struct ppp_if	*wan = (struct ppp_if *)ifnet_softc(ifp);
316    struct ip 		*ip;
317	struct udphdr	udp;
318    u_int32_t       val4;
319
320	// Wcast-align fixes - use memcmp and memcpy for unaligned accesses
321    ip = (struct ip *)(void*)pkt;
322    val4 = htonl(INADDR_BROADCAST);
323    if (!memcmp(&ip->ip_dst.s_addr, &val4, sizeof(struct in_addr)) && ip->ip_p == IPPROTO_UDP) {
324
325		memcpy(&udp, pkt + sizeof(struct ip), sizeof(struct udphdr));
326		if (udp.uh_sport == htons(IPPORT_BOOTPC) && udp.uh_dport == htons(IPPORT_BOOTPS)) {
327				return 1;
328		}
329	}
330
331	return 0;
332}
333
334
335