2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Sepherosa Ziehau <sepherosa@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include <sys/param.h>
37 #include <sys/kernel.h>
39 #include <sys/msgport.h>
40 #include <sys/socketvar.h>
41 #include <sys/sysctl.h>
44 #include <net/if_var.h>
45 #include <net/route.h>
46 #include <net/ethernet.h>
47 #include <net/netisr2.h>
48 #include <net/netmsg2.h>
50 #include <netinet/in.h>
51 #include <netinet/in_var.h>
52 #include <netinet/ip.h>
53 #include <netinet/ip_var.h>
55 #include <net/ipfw/ip_fw2.h>
56 #include <net/dummynet/ip_dummynet.h>
58 static void ip_dn_ether_output(netmsg_t
);
59 static void ip_dn_ether_demux(netmsg_t
);
60 static void ip_dn_ip_input(netmsg_t
);
61 static void ip_dn_ip_output(netmsg_t
);
63 static void ip_dn_sockopt_dispatch(netmsg_t
);
64 static void ip_dn_freepkt_dispatch(netmsg_t
);
65 static void ip_dn_dispatch(netmsg_t
);
67 static void ip_dn_freepkt(struct dn_pkt
*);
69 static int ip_dn_sockopt_flush(struct sockopt
*);
70 static int ip_dn_sockopt_get(struct sockopt
*);
71 static int ip_dn_sockopt_config(struct sockopt
*);
73 ip_dn_io_t
*ip_dn_io_ptr
;
76 TUNABLE_INT("net.inet.ip.dummynet.cpu", &ip_dn_cpu
);
78 SYSCTL_NODE(_net_inet_ip
, OID_AUTO
, dummynet
, CTLFLAG_RW
, 0, "Dummynet");
79 SYSCTL_INT(_net_inet_ip_dummynet
, OID_AUTO
, cpu
, CTLFLAG_RD
,
80 &ip_dn_cpu
, 0, "CPU to run dummynet");
83 ip_dn_queue(struct mbuf
*m
)
85 struct netmsg_packet
*nmp
;
89 KASSERT(m
->m_pkthdr
.fw_flags
& DUMMYNET_MBUF_TAGGED
,
90 ("mbuf is not tagged for dummynet!"));
92 nmp
= &m
->m_hdr
.mh_netmsg
;
93 netmsg_init(&nmp
->base
, NULL
, &netisr_apanic_rport
,
97 port
= netisr_cpuport(ip_dn_cpu
);
98 lwkt_sendmsg(port
, &nmp
->base
.lmsg
);
102 ip_dn_packet_free(struct dn_pkt
*pkt
)
104 struct netmsg_packet
*nmp
;
105 struct mbuf
*m
= pkt
->dn_m
;
108 KASSERT(m
->m_pkthdr
.fw_flags
& DUMMYNET_MBUF_TAGGED
,
109 ("mbuf is not tagged for dummynet!"));
111 nmp
= &m
->m_hdr
.mh_netmsg
;
112 netmsg_init(&nmp
->base
, NULL
, &netisr_apanic_rport
,
113 0, ip_dn_freepkt_dispatch
);
116 lwkt_sendmsg(pkt
->msgport
, &nmp
->base
.lmsg
);
120 ip_dn_packet_redispatch(struct dn_pkt
*pkt
)
122 static const netisr_fn_t dispatches
[DN_TO_MAX
] = {
123 [DN_TO_IP_OUT
] = ip_dn_ip_output
,
124 [DN_TO_IP_IN
] = ip_dn_ip_input
,
125 [DN_TO_ETH_DEMUX
] = ip_dn_ether_demux
,
126 [DN_TO_ETH_OUT
] = ip_dn_ether_output
129 struct netmsg_packet
*nmp
;
131 netisr_fn_t dispatch
;
134 dir
= (pkt
->dn_flags
& DN_FLAGS_DIR_MASK
);
135 KASSERT(dir
< DN_TO_MAX
,
136 ("unknown dummynet redispatch dir %d", dir
));
138 dispatch
= dispatches
[dir
];
139 KASSERT(dispatch
!= NULL
,
140 ("unsupported dummynet redispatch dir %d", dir
));
144 KASSERT(m
->m_pkthdr
.fw_flags
& DUMMYNET_MBUF_TAGGED
,
145 ("mbuf is not tagged for dummynet!"));
147 nmp
= &m
->m_hdr
.mh_netmsg
;
148 netmsg_init(&nmp
->base
, NULL
, &netisr_apanic_rport
, 0, dispatch
);
151 lwkt_sendmsg(pkt
->msgport
, &nmp
->base
.lmsg
);
155 ip_dn_sockopt(struct sockopt
*sopt
)
159 /* Disallow sets in really-really secure mode. */
160 if (sopt
->sopt_dir
== SOPT_SET
) {
161 if (securelevel
>= 3)
165 switch (sopt
->sopt_name
) {
166 case IP_DUMMYNET_GET
:
167 error
= ip_dn_sockopt_get(sopt
);
170 case IP_DUMMYNET_FLUSH
:
171 error
= ip_dn_sockopt_flush(sopt
);
174 case IP_DUMMYNET_DEL
:
175 case IP_DUMMYNET_CONFIGURE
:
176 error
= ip_dn_sockopt_config(sopt
);
180 kprintf("%s -- unknown option %d\n", __func__
, sopt
->sopt_name
);
188 ip_dn_freepkt(struct dn_pkt
*pkt
)
190 struct rtentry
*rt
= pkt
->ro
.ro_rt
;
192 /* Unreference route entry */
194 if (rt
->rt_refcnt
<= 0) { /* XXX assert? */
195 kprintf("-- warning, refcnt now %ld, decreasing\n",
201 /* Unreference packet private data */
202 if (pkt
->dn_unref_priv
)
203 pkt
->dn_unref_priv(pkt
->dn_priv
);
205 /* Free the parent mbuf, this will free 'pkt' as well */
210 ip_dn_freepkt_dispatch(netmsg_t nmsg
)
212 struct netmsg_packet
*nmp
;
220 KASSERT(m
->m_pkthdr
.fw_flags
& DUMMYNET_MBUF_TAGGED
,
221 ("mbuf is not tagged for dummynet!"));
223 mtag
= m_tag_find(m
, PACKET_TAG_DUMMYNET
, NULL
);
224 KKASSERT(mtag
!= NULL
);
226 pkt
= m_tag_data(mtag
);
227 KASSERT(pkt
->cpuid
== mycpuid
,
228 ("%s: dummynet packet was delivered to wrong cpu! "
229 "target cpuid %d, mycpuid %d", __func__
,
230 pkt
->cpuid
, mycpuid
));
236 ip_dn_dispatch(netmsg_t nmsg
)
238 struct netmsg_packet
*nmp
;
243 KASSERT(ip_dn_cpu
== mycpuid
,
244 ("%s: dummynet packet was delivered to wrong cpu! "
245 "dummynet cpuid %d, mycpuid %d", __func__
,
246 ip_dn_cpu
, mycpuid
));
251 KASSERT(m
->m_pkthdr
.fw_flags
& DUMMYNET_MBUF_TAGGED
,
252 ("mbuf is not tagged for dummynet!"));
254 if (DUMMYNET_LOADED
) {
255 if (ip_dn_io_ptr(m
) == 0)
260 * ip_dn_io_ptr() failed or dummynet(4) is not loaded
262 mtag
= m_tag_find(m
, PACKET_TAG_DUMMYNET
, NULL
);
263 KKASSERT(mtag
!= NULL
);
265 pkt
= m_tag_data(mtag
);
266 ip_dn_packet_free(pkt
);
270 ip_dn_ip_output(netmsg_t nmsg
)
272 struct netmsg_packet
*nmp
;
277 ip_dn_unref_priv_t unref_priv
;
283 KASSERT(m
->m_pkthdr
.fw_flags
& DUMMYNET_MBUF_TAGGED
,
284 ("mbuf is not tagged for dummynet!"));
286 mtag
= m_tag_find(m
, PACKET_TAG_DUMMYNET
, NULL
);
287 KKASSERT(mtag
!= NULL
);
289 pkt
= m_tag_data(mtag
);
290 KASSERT(pkt
->cpuid
== mycpuid
,
291 ("%s: dummynet packet was delivered to wrong cpu! "
292 "target cpuid %d, mycpuid %d", __func__
,
293 pkt
->cpuid
, mycpuid
));
294 KASSERT((pkt
->dn_flags
& DN_FLAGS_DIR_MASK
) == DN_TO_IP_OUT
,
295 ("wrong direction %d, should be %d",
296 (pkt
->dn_flags
& DN_FLAGS_DIR_MASK
), DN_TO_IP_OUT
));
299 unref_priv
= pkt
->dn_unref_priv
;
302 if (rt
!= NULL
&& !(rt
->rt_flags
& RTF_UP
)) {
304 * Recorded rtentry is gone, when the packet
311 ip_output(pkt
->dn_m
, NULL
, NULL
, pkt
->flags
, NULL
, NULL
);
312 /* 'rt' will be freed in ip_output */
319 ip_dn_ip_input(netmsg_t nmsg
)
321 struct netmsg_packet
*nmp
;
325 ip_dn_unref_priv_t unref_priv
;
331 KASSERT(m
->m_pkthdr
.fw_flags
& DUMMYNET_MBUF_TAGGED
,
332 ("mbuf is not tagged for dummynet!"));
334 mtag
= m_tag_find(m
, PACKET_TAG_DUMMYNET
, NULL
);
335 KKASSERT(mtag
!= NULL
);
337 pkt
= m_tag_data(mtag
);
338 KASSERT(pkt
->cpuid
== mycpuid
,
339 ("%s: dummynet packet was delivered to wrong cpu! "
340 "target cpuid %d, mycpuid %d", __func__
,
341 pkt
->cpuid
, mycpuid
));
342 KASSERT(pkt
->ro
.ro_rt
== NULL
,
343 ("route entry is not NULL for ip_input"));
344 KASSERT((pkt
->dn_flags
& DN_FLAGS_DIR_MASK
) == DN_TO_IP_IN
,
345 ("wrong direction %d, should be %d",
346 (pkt
->dn_flags
& DN_FLAGS_DIR_MASK
), DN_TO_IP_IN
));
349 unref_priv
= pkt
->dn_unref_priv
;
358 ip_dn_ether_demux(netmsg_t nmsg
)
360 struct netmsg_packet
*nmp
;
364 ip_dn_unref_priv_t unref_priv
;
370 KASSERT(m
->m_pkthdr
.fw_flags
& DUMMYNET_MBUF_TAGGED
,
371 ("mbuf is not tagged for dummynet!"));
373 mtag
= m_tag_find(m
, PACKET_TAG_DUMMYNET
, NULL
);
374 KKASSERT(mtag
!= NULL
);
376 pkt
= m_tag_data(mtag
);
377 KASSERT(pkt
->cpuid
== mycpuid
,
378 ("%s: dummynet packet was delivered to wrong cpu! "
379 "target cpuid %d, mycpuid %d", __func__
,
380 pkt
->cpuid
, mycpuid
));
381 KASSERT(pkt
->ro
.ro_rt
== NULL
,
382 ("route entry is not NULL for ether_demux"));
383 KASSERT((pkt
->dn_flags
& DN_FLAGS_DIR_MASK
) == DN_TO_ETH_DEMUX
,
384 ("wrong direction %d, should be %d",
385 (pkt
->dn_flags
& DN_FLAGS_DIR_MASK
), DN_TO_ETH_DEMUX
));
388 unref_priv
= pkt
->dn_unref_priv
;
391 * Make sure that ether header is contiguous
393 if (m
->m_len
< ETHER_HDR_LEN
&&
394 (m
= m_pullup(m
, ETHER_HDR_LEN
)) == NULL
) {
395 kprintf("%s: pullup fail, dropping pkt\n", __func__
);
398 ether_demux_oncpu(m
->m_pkthdr
.rcvif
, m
);
405 ip_dn_ether_output(netmsg_t nmsg
)
407 struct netmsg_packet
*nmp
;
411 ip_dn_unref_priv_t unref_priv
;
417 KASSERT(m
->m_pkthdr
.fw_flags
& DUMMYNET_MBUF_TAGGED
,
418 ("mbuf is not tagged for dummynet!"));
420 mtag
= m_tag_find(m
, PACKET_TAG_DUMMYNET
, NULL
);
421 KKASSERT(mtag
!= NULL
);
423 pkt
= m_tag_data(mtag
);
424 KASSERT(pkt
->cpuid
== mycpuid
,
425 ("%s: dummynet packet was delivered to wrong cpu! "
426 "target cpuid %d, mycpuid %d", __func__
,
427 pkt
->cpuid
, mycpuid
));
428 KASSERT(pkt
->ro
.ro_rt
== NULL
,
429 ("route entry is not NULL for ether_output_frame"));
430 KASSERT((pkt
->dn_flags
& DN_FLAGS_DIR_MASK
) == DN_TO_ETH_OUT
,
431 ("wrong direction %d, should be %d",
432 (pkt
->dn_flags
& DN_FLAGS_DIR_MASK
), DN_TO_ETH_OUT
));
435 unref_priv
= pkt
->dn_unref_priv
;
437 ether_output_frame(pkt
->ifp
, m
);
444 ip_dn_sockopt_dispatch(netmsg_t nmsg
)
446 lwkt_msg
*msg
= &nmsg
->lmsg
;
447 struct dn_sopt
*dn_sopt
= msg
->u
.ms_resultp
;
450 KASSERT(ip_dn_cpu
== mycpuid
,
451 ("%s: dummynet sockopt is done on wrong cpu! "
452 "dummynet cpuid %d, mycpuid %d", __func__
,
453 ip_dn_cpu
, mycpuid
));
456 error
= ip_dn_ctl_ptr(dn_sopt
);
459 lwkt_replymsg(msg
, error
);
463 ip_dn_sockopt_flush(struct sockopt
*sopt
)
465 struct dn_sopt dn_sopt
;
466 struct netmsg_base smsg
;
468 bzero(&dn_sopt
, sizeof(dn_sopt
));
469 dn_sopt
.dn_sopt_name
= sopt
->sopt_name
;
471 netmsg_init(&smsg
, NULL
, &curthread
->td_msgport
,
472 0, ip_dn_sockopt_dispatch
);
473 smsg
.lmsg
.u
.ms_resultp
= &dn_sopt
;
474 lwkt_domsg(netisr_cpuport(ip_dn_cpu
), &smsg
.lmsg
, 0);
476 return smsg
.lmsg
.ms_error
;
480 ip_dn_sockopt_get(struct sockopt
*sopt
)
482 struct dn_sopt dn_sopt
;
483 struct netmsg_base smsg
;
486 bzero(&dn_sopt
, sizeof(dn_sopt
));
487 dn_sopt
.dn_sopt_name
= sopt
->sopt_name
;
489 netmsg_init(&smsg
, NULL
, &curthread
->td_msgport
,
490 0, ip_dn_sockopt_dispatch
);
491 smsg
.lmsg
.u
.ms_resultp
= &dn_sopt
;
492 lwkt_domsg(netisr_cpuport(ip_dn_cpu
), &smsg
.lmsg
, 0);
494 error
= smsg
.lmsg
.ms_error
;
496 KKASSERT(dn_sopt
.dn_sopt_arg
== NULL
);
497 KKASSERT(dn_sopt
.dn_sopt_arglen
== 0);
501 soopt_from_kbuf(sopt
, dn_sopt
.dn_sopt_arg
, dn_sopt
.dn_sopt_arglen
);
502 kfree(dn_sopt
.dn_sopt_arg
, M_TEMP
);
507 ip_dn_sockopt_config(struct sockopt
*sopt
)
509 struct dn_ioc_pipe tmp_ioc_pipe
;
510 struct dn_sopt dn_sopt
;
511 struct netmsg_base smsg
;
514 error
= soopt_to_kbuf(sopt
, &tmp_ioc_pipe
, sizeof tmp_ioc_pipe
,
515 sizeof tmp_ioc_pipe
);
519 bzero(&dn_sopt
, sizeof(dn_sopt
));
520 dn_sopt
.dn_sopt_name
= sopt
->sopt_name
;
521 dn_sopt
.dn_sopt_arg
= &tmp_ioc_pipe
;
522 dn_sopt
.dn_sopt_arglen
= sizeof(tmp_ioc_pipe
);
524 netmsg_init(&smsg
, NULL
, &curthread
->td_msgport
,
525 0, ip_dn_sockopt_dispatch
);
526 smsg
.lmsg
.u
.ms_resultp
= &dn_sopt
;
527 lwkt_domsg(netisr_cpuport(ip_dn_cpu
), &smsg
.lmsg
, 0);
529 return smsg
.lmsg
.ms_error
;