#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sockio.h>
#include <kern/locks.h>
#include <net/if.h>
#include <net/kpi_protocol.h>
#include <machine/spl.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/bootp.h>
#include "ppp_defs.h" // public ppp values
#include "ppp_ip.h"
#include "ppp_domain.h"
#include "ppp_if.h"
#include "if_ppplink.h"
static errno_t ppp_ip_input(ifnet_t ifp, protocol_family_t protocol,
mbuf_t packet, char* header);
static errno_t ppp_ip_preoutput(ifnet_t ifp, protocol_family_t protocol,
mbuf_t *packet, const struct sockaddr *dest,
void *route, char *frame_type, char *link_layer_dest);
static errno_t ppp_ip_ioctl(ifnet_t ifp, protocol_family_t protocol,
u_int32_t command, void* argument);
extern lck_mtx_t *ppp_domain_mutex;
int ppp_ip_init(int init_arg)
{
return proto_register_plumber(PF_INET, APPLE_IF_FAM_PPP,
ppp_ip_attach, ppp_ip_detach);
}
int ppp_ip_dispose(int term_arg)
{
proto_unregister_plumber(PF_INET, APPLE_IF_FAM_PPP);
return 0;
}
errno_t ppp_ip_attach(ifnet_t ifp, protocol_family_t protocol)
{
int ret;
struct ifnet_attach_proto_param reg;
struct ppp_if *wan = (struct ppp_if *)ifnet_softc(ifp);
LOGDBG(ifp, (LOGVAL, "ppp_ip_attach: name = %s, unit = %d\n", ifnet_name(ifp), ifnet_unit(ifp)));
if (wan->ip_attached)
return 0;
bzero(®, sizeof(struct ifnet_attach_proto_param));
reg.input = ppp_ip_input;
reg.pre_output = ppp_ip_preoutput;
reg.ioctl = ppp_ip_ioctl;
ret = ifnet_attach_protocol(ifp, PF_INET, ®);
LOGRETURN(ret, ret, "ppp_ip_attach: ifnet_attach_protocol error = 0x%x\n");
LOGDBG(ifp, (LOGVAL, "ppp_i6_attach: ifnet_attach_protocol family = 0x%x\n", protocol));
ifnet_find_by_name("lo0", &wan->lo_ifp);
wan->ip_attached = 1;
return 0;
}
void ppp_ip_detach(ifnet_t ifp, protocol_family_t protocol)
{
int ret;
struct ppp_if *wan = (struct ppp_if *)ifnet_softc(ifp);
LOGDBG(ifp, (LOGVAL, "ppp_ip_detach\n"));
if (!wan->ip_attached)
return;
ifnet_release(wan->lo_ifp);
wan->lo_ifp = 0;
ret = ifnet_detach_protocol(ifp, PF_INET);
if (ret)
log(LOGVAL, "ppp_ip_detach: ifnet_detach_protocol error = 0x%x\n", ret);
wan->ip_attached = 0;
}
errno_t ppp_ip_ioctl(ifnet_t ifp, protocol_family_t protocol,
u_int32_t command, void* argument)
{
struct ifaddr *ifa = (struct ifaddr *)argument;
struct ppp_if *wan = (struct ppp_if *)ifnet_softc(ifp);
struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr;
int error = 0;
switch (command) {
case SIOCSIFADDR:
case SIOCAIFADDR:
LOGDBG(ifp, (LOGVAL, "ppp_ip_ioctl: cmd = SIOCSIFADDR/SIOCAIFADDR\n"));
if (ifa->ifa_addr->sa_family != AF_INET) {
error = EAFNOSUPPORT;
break;
}
wan->ip_src.s_addr = addr->sin_addr.s_addr;
addr++;
wan->ip_dst.s_addr = addr->sin_addr.s_addr;
break;
default :
error = EOPNOTSUPP;
}
return error;
}
errno_t ppp_ip_input(ifnet_t ifp, protocol_family_t protocol,
mbuf_t packet, char* header)
{
LOGMBUF("ppp_ip_input", packet);
if (ipflow_fastforward((struct mbuf *)packet)) {
return 0;
}
if (proto_input(PF_INET, packet))
mbuf_freem(packet);
return 0;
}
errno_t ppp_ip_preoutput(ifnet_t ifp, protocol_family_t protocol,
mbuf_t *packet, const struct sockaddr *dest,
void *route, char *frame_type, char *link_layer_dest)
{
errno_t err;
struct ppp_if *wan = (struct ppp_if *)ifnet_softc(ifp);
LOGMBUF("ppp_ip_preoutput", *packet);
lck_mtx_lock(ppp_domain_mutex);
#if 0
(*packet)->m_flags &= ~M_HIGHPRI;
ip = mtod(*packet, struct ip *);
if (ip->ip_tos & IPTOS_LOWDELAY)
(*packet)->m_flags |= M_HIGHPRI;
#endif
if ((wan->sc_flags & SC_LOOP_LOCAL)
&& (((struct sockaddr_in *)dest)->sin_addr.s_addr == wan->ip_src.s_addr)
&& wan->lo_ifp) {
err = ifnet_output(wan->lo_ifp, PF_INET, *packet, 0, (struct sockaddr *)dest);
lck_mtx_unlock(ppp_domain_mutex);
return (err ? err : EJUSTRETURN);
}
lck_mtx_unlock(ppp_domain_mutex);
*(u_int16_t *)frame_type = PPP_IP;
return 0;
}
int ppp_ip_af_src_out(ifnet_t ifp, char *pkt)
{
struct ppp_if *wan = (struct ppp_if *)ifnet_softc(ifp);
struct ip *ip;
ip = (struct ip *)pkt;
return (ip->ip_src.s_addr != wan->ip_src.s_addr);
}
int ppp_ip_af_src_in(ifnet_t ifp, char *pkt)
{
struct ppp_if *wan = (struct ppp_if *)ifnet_softc(ifp);
struct ip *ip;
ip = (struct ip *)pkt;
return (ip->ip_src.s_addr != wan->ip_dst.s_addr);
}
int ppp_ip_bootp_client_in(ifnet_t ifp, char *pkt)
{
struct ppp_if *wan = (struct ppp_if *)ifnet_softc(ifp);
struct ip *ip;
struct udphdr *udp;
ip = (struct ip *)pkt;
if (ip->ip_dst.s_addr == wan->ip_src.s_addr && ip->ip_p == IPPROTO_UDP) {
udp = (struct udphdr *)(pkt + sizeof(struct ip));
if (udp->uh_sport == htons(IPPORT_BOOTPS) && udp->uh_dport == htons(IPPORT_BOOTPC)) {
return 1;
}
}
return 0;
}
int ppp_ip_bootp_server_in(ifnet_t ifp, char *pkt)
{
struct ip *ip;
struct udphdr *udp;
ip = (struct ip *)pkt;
if (ip->ip_dst.s_addr == htonl(INADDR_BROADCAST) && ip->ip_p == IPPROTO_UDP) {
udp = (struct udphdr *)(pkt + sizeof(struct ip));
if (udp->uh_sport == htons(IPPORT_BOOTPC) && udp->uh_dport == htons(IPPORT_BOOTPS)) {
return 1;
}
}
return 0;
}