#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/msgport.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <net/ethernet.h>
#include <net/netisr2.h>
#include <net/netmsg2.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <net/dummynet/ip_dummynet.h>
static void ip_dn_ether_output(netmsg_t);
static void ip_dn_ether_demux(netmsg_t);
static void ip_dn_ip_input(netmsg_t);
static void ip_dn_ip_output(netmsg_t);
static void ip_dn_sockopt_dispatch(netmsg_t);
static void ip_dn_freepkt_dispatch(netmsg_t);
static void ip_dn_dispatch(netmsg_t);
static void ip_dn_freepkt(struct dn_pkt *);
static int ip_dn_sockopt_flush(struct sockopt *);
static int ip_dn_sockopt_get(struct sockopt *);
static int ip_dn_sockopt_config(struct sockopt *);
ip_dn_io_t *ip_dn_io_ptr;
ip_dn_ctl_t *ip_dn_ctl_ptr;
int ip_dn_cpu = 0;
TUNABLE_INT("net.inet.ip.dummynet.cpu", &ip_dn_cpu);
SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet");
SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, cpu, CTLFLAG_RD,
&ip_dn_cpu, 0, "CPU to run dummynet");
void
ip_dn_queue(struct mbuf *m)
{
struct netmsg_packet *nmp;
lwkt_port_t port;
M_ASSERTPKTHDR(m);
KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
("mbuf is not tagged for dummynet!"));
nmp = &m->m_hdr.mh_netmsg;
netmsg_init(&nmp->base, NULL, &netisr_apanic_rport,
0, ip_dn_dispatch);
nmp->nm_packet = m;
port = netisr_cpuport(ip_dn_cpu);
lwkt_sendmsg(port, &nmp->base.lmsg);
}
void
ip_dn_packet_free(struct dn_pkt *pkt)
{
struct netmsg_packet *nmp;
struct mbuf *m = pkt->dn_m;
M_ASSERTPKTHDR(m);
KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
("mbuf is not tagged for dummynet!"));
nmp = &m->m_hdr.mh_netmsg;
netmsg_init(&nmp->base, NULL, &netisr_apanic_rport,
0, ip_dn_freepkt_dispatch);
nmp->nm_packet = m;
lwkt_sendmsg(pkt->msgport, &nmp->base.lmsg);
}
void
ip_dn_packet_redispatch(struct dn_pkt *pkt)
{
static const netisr_fn_t dispatches[DN_TO_MAX] = {
[DN_TO_IP_OUT] = ip_dn_ip_output,
[DN_TO_IP_IN] = ip_dn_ip_input,
[DN_TO_ETH_DEMUX] = ip_dn_ether_demux,
[DN_TO_ETH_OUT] = ip_dn_ether_output
};
struct netmsg_packet *nmp;
struct mbuf *m;
netisr_fn_t dispatch;
int dir;
dir = (pkt->dn_flags & DN_FLAGS_DIR_MASK);
KASSERT(dir < DN_TO_MAX,
("unknown dummynet redispatch dir %d", dir));
dispatch = dispatches[dir];
KASSERT(dispatch != NULL,
("unsupported dummynet redispatch dir %d", dir));
m = pkt->dn_m;
M_ASSERTPKTHDR(m);
KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
("mbuf is not tagged for dummynet!"));
nmp = &m->m_hdr.mh_netmsg;
netmsg_init(&nmp->base, NULL, &netisr_apanic_rport, 0, dispatch);
nmp->nm_packet = m;
lwkt_sendmsg(pkt->msgport, &nmp->base.lmsg);
}
int
ip_dn_sockopt(struct sockopt *sopt)
{
int error = 0;
if (sopt->sopt_dir == SOPT_SET) {
if (securelevel >= 3)
return EPERM;
}
switch (sopt->sopt_name) {
case IP_DUMMYNET_GET:
error = ip_dn_sockopt_get(sopt);
break;
case IP_DUMMYNET_FLUSH:
error = ip_dn_sockopt_flush(sopt);
break;
case IP_DUMMYNET_DEL:
case IP_DUMMYNET_CONFIGURE:
error = ip_dn_sockopt_config(sopt);
break;
default:
kprintf("%s -- unknown option %d\n", __func__, sopt->sopt_name);
error = EINVAL;
break;
}
return error;
}
static void
ip_dn_freepkt(struct dn_pkt *pkt)
{
struct rtentry *rt = pkt->ro.ro_rt;
if (rt != NULL) {
if (rt->rt_refcnt <= 0) {
kprintf("-- warning, refcnt now %ld, decreasing\n",
rt->rt_refcnt);
}
RTFREE(rt);
}
if (pkt->dn_unref_priv)
pkt->dn_unref_priv(pkt->dn_priv);
m_freem(pkt->dn_m);
}
static void
ip_dn_freepkt_dispatch(netmsg_t nmsg)
{
struct netmsg_packet *nmp;
struct mbuf *m;
struct m_tag *mtag;
struct dn_pkt *pkt;
nmp = &nmsg->packet;
m = nmp->nm_packet;
M_ASSERTPKTHDR(m);
KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
("mbuf is not tagged for dummynet!"));
mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
KKASSERT(mtag != NULL);
pkt = m_tag_data(mtag);
KASSERT(pkt->cpuid == mycpuid,
("%s: dummynet packet was delivered to wrong cpu! "
"target cpuid %d, mycpuid %d", __func__,
pkt->cpuid, mycpuid));
ip_dn_freepkt(pkt);
}
static void
ip_dn_dispatch(netmsg_t nmsg)
{
struct netmsg_packet *nmp;
struct mbuf *m;
struct m_tag *mtag;
struct dn_pkt *pkt;
KASSERT(ip_dn_cpu == mycpuid,
("%s: dummynet packet was delivered to wrong cpu! "
"dummynet cpuid %d, mycpuid %d", __func__,
ip_dn_cpu, mycpuid));
nmp = &nmsg->packet;
m = nmp->nm_packet;
M_ASSERTPKTHDR(m);
KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
("mbuf is not tagged for dummynet!"));
if (DUMMYNET_LOADED) {
if (ip_dn_io_ptr(m) == 0)
return;
}
mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
KKASSERT(mtag != NULL);
pkt = m_tag_data(mtag);
ip_dn_packet_free(pkt);
}
static void
ip_dn_ip_output(netmsg_t nmsg)
{
struct netmsg_packet *nmp;
struct mbuf *m;
struct m_tag *mtag;
struct dn_pkt *pkt;
struct rtentry *rt;
ip_dn_unref_priv_t unref_priv;
void *priv;
nmp = &nmsg->packet;
m = nmp->nm_packet;
M_ASSERTPKTHDR(m);
KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
("mbuf is not tagged for dummynet!"));
mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
KKASSERT(mtag != NULL);
pkt = m_tag_data(mtag);
KASSERT(pkt->cpuid == mycpuid,
("%s: dummynet packet was delivered to wrong cpu! "
"target cpuid %d, mycpuid %d", __func__,
pkt->cpuid, mycpuid));
KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_OUT,
("wrong direction %d, should be %d",
(pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_OUT));
priv = pkt->dn_priv;
unref_priv = pkt->dn_unref_priv;
rt = pkt->ro.ro_rt;
if (rt != NULL && !(rt->rt_flags & RTF_UP)) {
ip_dn_freepkt(pkt);
return;
}
ip_output(pkt->dn_m, NULL, NULL, pkt->flags, NULL, NULL);
if (unref_priv)
unref_priv(priv);
}
static void
ip_dn_ip_input(netmsg_t nmsg)
{
struct netmsg_packet *nmp;
struct mbuf *m;
struct m_tag *mtag;
struct dn_pkt *pkt;
ip_dn_unref_priv_t unref_priv;
void *priv;
struct ip *ip __debugvar;
nmp = &nmsg->packet;
m = nmp->nm_packet;
M_ASSERTPKTHDR(m);
KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
("mbuf is not tagged for dummynet!"));
mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
KKASSERT(mtag != NULL);
pkt = m_tag_data(mtag);
KASSERT(pkt->cpuid == mycpuid,
("%s: dummynet packet was delivered to wrong cpu! "
"target cpuid %d, mycpuid %d", __func__,
pkt->cpuid, mycpuid));
KASSERT(pkt->ro.ro_rt == NULL,
("route entry is not NULL for ip_input"));
KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_IN,
("wrong direction %d, should be %d",
(pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_IN));
priv = pkt->dn_priv;
unref_priv = pkt->dn_unref_priv;
KKASSERT(m->m_len >= sizeof(*ip));
ip_input(m);
if (unref_priv)
unref_priv(priv);
}
static void
ip_dn_ether_demux(netmsg_t nmsg)
{
struct netmsg_packet *nmp;
struct mbuf *m;
struct m_tag *mtag;
struct dn_pkt *pkt;
ip_dn_unref_priv_t unref_priv;
void *priv;
nmp = &nmsg->packet;
m = nmp->nm_packet;
M_ASSERTPKTHDR(m);
KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
("mbuf is not tagged for dummynet!"));
mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
KKASSERT(mtag != NULL);
pkt = m_tag_data(mtag);
KASSERT(pkt->cpuid == mycpuid,
("%s: dummynet packet was delivered to wrong cpu! "
"target cpuid %d, mycpuid %d", __func__,
pkt->cpuid, mycpuid));
KASSERT(pkt->ro.ro_rt == NULL,
("route entry is not NULL for ether_demux"));
KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_DEMUX,
("wrong direction %d, should be %d",
(pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_DEMUX));
priv = pkt->dn_priv;
unref_priv = pkt->dn_unref_priv;
if (m->m_len < ETHER_HDR_LEN &&
(m = m_pullup(m, ETHER_HDR_LEN)) == NULL) {
kprintf("%s: pullup fail, dropping pkt\n", __func__);
goto back;
}
ether_demux_oncpu(m->m_pkthdr.rcvif, m);
back:
if (unref_priv)
unref_priv(priv);
}
static void
ip_dn_ether_output(netmsg_t nmsg)
{
struct netmsg_packet *nmp;
struct mbuf *m;
struct m_tag *mtag;
struct dn_pkt *pkt;
ip_dn_unref_priv_t unref_priv;
void *priv;
nmp = &nmsg->packet;
m = nmp->nm_packet;
M_ASSERTPKTHDR(m);
KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
("mbuf is not tagged for dummynet!"));
mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
KKASSERT(mtag != NULL);
pkt = m_tag_data(mtag);
KASSERT(pkt->cpuid == mycpuid,
("%s: dummynet packet was delivered to wrong cpu! "
"target cpuid %d, mycpuid %d", __func__,
pkt->cpuid, mycpuid));
KASSERT(pkt->ro.ro_rt == NULL,
("route entry is not NULL for ether_output_frame"));
KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_OUT,
("wrong direction %d, should be %d",
(pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_OUT));
priv = pkt->dn_priv;
unref_priv = pkt->dn_unref_priv;
ether_output_frame(pkt->ifp, m);
if (unref_priv)
unref_priv(priv);
}
static void
ip_dn_sockopt_dispatch(netmsg_t nmsg)
{
lwkt_msg *msg = &nmsg->lmsg;
struct dn_sopt *dn_sopt = msg->u.ms_resultp;
int error;
KASSERT(ip_dn_cpu == mycpuid,
("%s: dummynet sockopt is done on wrong cpu! "
"dummynet cpuid %d, mycpuid %d", __func__,
ip_dn_cpu, mycpuid));
if (DUMMYNET_LOADED)
error = ip_dn_ctl_ptr(dn_sopt);
else
error = ENOPROTOOPT;
lwkt_replymsg(msg, error);
}
static int
ip_dn_sockopt_flush(struct sockopt *sopt)
{
struct dn_sopt dn_sopt;
struct netmsg_base smsg;
bzero(&dn_sopt, sizeof(dn_sopt));
dn_sopt.dn_sopt_name = sopt->sopt_name;
netmsg_init(&smsg, NULL, &curthread->td_msgport,
0, ip_dn_sockopt_dispatch);
smsg.lmsg.u.ms_resultp = &dn_sopt;
lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0);
return smsg.lmsg.ms_error;
}
static int
ip_dn_sockopt_get(struct sockopt *sopt)
{
struct dn_sopt dn_sopt;
struct netmsg_base smsg;
int error;
bzero(&dn_sopt, sizeof(dn_sopt));
dn_sopt.dn_sopt_name = sopt->sopt_name;
netmsg_init(&smsg, NULL, &curthread->td_msgport,
0, ip_dn_sockopt_dispatch);
smsg.lmsg.u.ms_resultp = &dn_sopt;
lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0);
error = smsg.lmsg.ms_error;
if (error) {
KKASSERT(dn_sopt.dn_sopt_arg == NULL);
KKASSERT(dn_sopt.dn_sopt_arglen == 0);
return error;
}
soopt_from_kbuf(sopt, dn_sopt.dn_sopt_arg, dn_sopt.dn_sopt_arglen);
kfree(dn_sopt.dn_sopt_arg, M_TEMP);
return 0;
}
static int
ip_dn_sockopt_config(struct sockopt *sopt)
{
struct dn_ioc_pipe tmp_ioc_pipe;
struct dn_sopt dn_sopt;
struct netmsg_base smsg;
int error;
error = soopt_to_kbuf(sopt, &tmp_ioc_pipe, sizeof tmp_ioc_pipe,
sizeof tmp_ioc_pipe);
if (error)
return error;
bzero(&dn_sopt, sizeof(dn_sopt));
dn_sopt.dn_sopt_name = sopt->sopt_name;
dn_sopt.dn_sopt_arg = &tmp_ioc_pipe;
dn_sopt.dn_sopt_arglen = sizeof(tmp_ioc_pipe);
netmsg_init(&smsg, NULL, &curthread->td_msgport,
0, ip_dn_sockopt_dispatch);
smsg.lmsg.u.ms_resultp = &dn_sopt;
lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0);
return smsg.lmsg.ms_error;
}