1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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
16  *    distribution.
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.
20  *
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
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/mbuf.h>
38 #include <sys/msgport.h>
39 #include <sys/socketvar.h>
40 #include <sys/sysctl.h>
41 
42 #include <net/if.h>
43 #include <net/if_var.h>
44 #include <net/route.h>
45 #include <net/ethernet.h>
46 #include <net/netisr2.h>
47 #include <net/netmsg2.h>
48 
49 #include <netinet/in.h>
50 #include <netinet/in_var.h>
51 #include <netinet/ip.h>
52 #include <netinet/ip_var.h>
53 
54 #include <net/dummynet/ip_dummynet.h>
55 
56 static void	ip_dn_ether_output(netmsg_t);
57 static void	ip_dn_ether_demux(netmsg_t);
58 static void	ip_dn_ip_input(netmsg_t);
59 static void	ip_dn_ip_output(netmsg_t);
60 
61 static void	ip_dn_sockopt_dispatch(netmsg_t);
62 static void	ip_dn_freepkt_dispatch(netmsg_t);
63 static void	ip_dn_dispatch(netmsg_t);
64 
65 static void	ip_dn_freepkt(struct dn_pkt *);
66 
67 static int	ip_dn_sockopt_flush(struct sockopt *);
68 static int	ip_dn_sockopt_get(struct sockopt *);
69 static int	ip_dn_sockopt_config(struct sockopt *);
70 
71 ip_dn_io_t	*ip_dn_io_ptr;
72 int		ip_dn_cpu = 0;
73 
74 TUNABLE_INT("net.inet.ip.dummynet.cpu", &ip_dn_cpu);
75 
76 SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet");
77 SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, cpu, CTLFLAG_RD,
78 	   &ip_dn_cpu, 0, "CPU to run dummynet");
79 
80 void
81 ip_dn_queue(struct mbuf *m)
82 {
83 	struct netmsg_packet *nmp;
84 	lwkt_port_t port;
85 
86 	M_ASSERTPKTHDR(m);
87 	KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
88 		("mbuf is not tagged for dummynet!"));
89 
90 	nmp = &m->m_hdr.mh_netmsg;
91 	netmsg_init(&nmp->base, NULL, &netisr_apanic_rport,
92 		    0, ip_dn_dispatch);
93 	nmp->nm_packet = m;
94 
95 	port = netisr_cpuport(ip_dn_cpu);
96 	lwkt_sendmsg(port, &nmp->base.lmsg);
97 }
98 
99 void
100 ip_dn_packet_free(struct dn_pkt *pkt)
101 {
102 	struct netmsg_packet *nmp;
103 	struct mbuf *m = pkt->dn_m;
104 
105 	M_ASSERTPKTHDR(m);
106 	KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
107 		("mbuf is not tagged for dummynet!"));
108 
109 	nmp = &m->m_hdr.mh_netmsg;
110 	netmsg_init(&nmp->base, NULL, &netisr_apanic_rport,
111 		    0, ip_dn_freepkt_dispatch);
112 	nmp->nm_packet = m;
113 
114 	lwkt_sendmsg(pkt->msgport, &nmp->base.lmsg);
115 }
116 
117 void
118 ip_dn_packet_redispatch(struct dn_pkt *pkt)
119 {
120 	static const netisr_fn_t dispatches[DN_TO_MAX] = {
121 	[DN_TO_IP_OUT]		= ip_dn_ip_output,
122 	[DN_TO_IP_IN]		= ip_dn_ip_input,
123 	[DN_TO_ETH_DEMUX]	= ip_dn_ether_demux,
124 	[DN_TO_ETH_OUT]		= ip_dn_ether_output
125 	};
126 
127 	struct netmsg_packet *nmp;
128 	struct mbuf *m;
129 	netisr_fn_t dispatch;
130 	int dir;
131 
132 	dir = (pkt->dn_flags & DN_FLAGS_DIR_MASK);
133 	KASSERT(dir < DN_TO_MAX,
134 		("unknown dummynet redispatch dir %d", dir));
135 
136 	dispatch = dispatches[dir];
137 	KASSERT(dispatch != NULL,
138 		("unsupported dummynet redispatch dir %d", dir));
139 
140 	m = pkt->dn_m;
141 	M_ASSERTPKTHDR(m);
142 	KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
143 		("mbuf is not tagged for dummynet!"));
144 
145 	nmp = &m->m_hdr.mh_netmsg;
146 	netmsg_init(&nmp->base, NULL, &netisr_apanic_rport, 0, dispatch);
147 	nmp->nm_packet = m;
148 
149 	lwkt_sendmsg(pkt->msgport, &nmp->base.lmsg);
150 }
151 
152 int
153 ip_dn_sockopt(struct sockopt *sopt)
154 {
155 	int error = 0;
156 
157 	/* Disallow sets in really-really secure mode. */
158 	if (sopt->sopt_dir == SOPT_SET) {
159 		if (securelevel >= 3)
160 			return EPERM;
161 	}
162 
163 	switch (sopt->sopt_name) {
164 	case IP_DUMMYNET_GET:
165 		error = ip_dn_sockopt_get(sopt);
166 		break;
167 
168 	case IP_DUMMYNET_FLUSH:
169 		error = ip_dn_sockopt_flush(sopt);
170 		break;
171 
172 	case IP_DUMMYNET_DEL:
173 	case IP_DUMMYNET_CONFIGURE:
174 		error = ip_dn_sockopt_config(sopt);
175 		break;
176 
177 	default:
178 		kprintf("%s -- unknown option %d\n", __func__, sopt->sopt_name);
179 		error = EINVAL;
180 		break;
181 	}
182 	return error;
183 }
184 
185 static void
186 ip_dn_freepkt(struct dn_pkt *pkt)
187 {
188 	struct rtentry *rt = pkt->ro.ro_rt;
189 
190 	/* Unreference route entry */
191 	if (rt != NULL) {
192 		if (rt->rt_refcnt <= 0) {	/* XXX assert? */
193 			kprintf("-- warning, refcnt now %ld, decreasing\n",
194 				rt->rt_refcnt);
195 		}
196 		RTFREE(rt);
197 	}
198 
199 	/* Unreference packet private data */
200 	if (pkt->dn_unref_priv)
201 		pkt->dn_unref_priv(pkt->dn_priv);
202 
203 	/* Free the parent mbuf, this will free 'pkt' as well */
204 	m_freem(pkt->dn_m);
205 }
206 
207 static void
208 ip_dn_freepkt_dispatch(netmsg_t nmsg)
209 {
210 	struct netmsg_packet *nmp;
211 	struct mbuf *m;
212 	struct m_tag *mtag;
213 	struct dn_pkt *pkt;
214 
215 	nmp = &nmsg->packet;
216 	m = nmp->nm_packet;
217 	M_ASSERTPKTHDR(m);
218 	KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
219 		("mbuf is not tagged for dummynet!"));
220 
221 	mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
222 	KKASSERT(mtag != NULL);
223 
224 	pkt = m_tag_data(mtag);
225 	KASSERT(pkt->cpuid == mycpuid,
226 		("%s: dummynet packet was delivered to wrong cpu! "
227 		 "target cpuid %d, mycpuid %d", __func__,
228 		 pkt->cpuid, mycpuid));
229 
230 	ip_dn_freepkt(pkt);
231 }
232 
233 static void
234 ip_dn_dispatch(netmsg_t nmsg)
235 {
236 	struct netmsg_packet *nmp;
237 	struct mbuf *m;
238 	struct m_tag *mtag;
239 	struct dn_pkt *pkt;
240 
241 	KASSERT(ip_dn_cpu == mycpuid,
242 		("%s: dummynet packet was delivered to wrong cpu! "
243 		 "dummynet cpuid %d, mycpuid %d", __func__,
244 		 ip_dn_cpu, mycpuid));
245 
246 	nmp = &nmsg->packet;
247 	m = nmp->nm_packet;
248 	M_ASSERTPKTHDR(m);
249 	KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
250 		("mbuf is not tagged for dummynet!"));
251 
252 	if (DUMMYNET_LOADED) {
253 		if (ip_dn_io_ptr(m) == 0)
254 			return;
255 	}
256 
257 	/*
258 	 * ip_dn_io_ptr() failed or dummynet(4) is not loaded
259 	 */
260 	mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
261 	KKASSERT(mtag != NULL);
262 
263 	pkt = m_tag_data(mtag);
264 	ip_dn_packet_free(pkt);
265 }
266 
267 static void
268 ip_dn_ip_output(netmsg_t nmsg)
269 {
270 	struct netmsg_packet *nmp;
271 	struct mbuf *m;
272 	struct m_tag *mtag;
273 	struct dn_pkt *pkt;
274 	struct rtentry *rt;
275 	ip_dn_unref_priv_t unref_priv;
276 	void *priv;
277 
278 	nmp = &nmsg->packet;
279 	m = nmp->nm_packet;
280 	M_ASSERTPKTHDR(m);
281 	KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
282 		("mbuf is not tagged for dummynet!"));
283 
284 	mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
285 	KKASSERT(mtag != NULL);
286 
287 	pkt = m_tag_data(mtag);
288 	KASSERT(pkt->cpuid == mycpuid,
289 		("%s: dummynet packet was delivered to wrong cpu! "
290 		 "target cpuid %d, mycpuid %d", __func__,
291 		 pkt->cpuid, mycpuid));
292 	KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_OUT,
293 		("wrong direction %d, should be %d",
294 		 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_OUT));
295 
296 	priv = pkt->dn_priv;
297 	unref_priv = pkt->dn_unref_priv;
298 	rt = pkt->ro.ro_rt;
299 
300 	if (rt != NULL && !(rt->rt_flags & RTF_UP)) {
301 		/*
302 		 * Recorded rtentry is gone, when the packet
303 		 * was on delay line.
304 		 */
305 		ip_dn_freepkt(pkt);
306 		return;
307 	}
308 
309 	ip_output(pkt->dn_m, NULL, NULL, pkt->flags, NULL, NULL);
310 	/* 'rt' will be freed in ip_output */
311 
312 	if (unref_priv)
313 		unref_priv(priv);
314 }
315 
316 static void
317 ip_dn_ip_input(netmsg_t nmsg)
318 {
319 	struct netmsg_packet *nmp;
320 	struct mbuf *m;
321 	struct m_tag *mtag;
322 	struct dn_pkt *pkt;
323 	ip_dn_unref_priv_t unref_priv;
324 	void *priv;
325 
326 	nmp = &nmsg->packet;
327 	m = nmp->nm_packet;
328 	M_ASSERTPKTHDR(m);
329 	KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
330 		("mbuf is not tagged for dummynet!"));
331 
332 	mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
333 	KKASSERT(mtag != NULL);
334 
335 	pkt = m_tag_data(mtag);
336 	KASSERT(pkt->cpuid == mycpuid,
337 		("%s: dummynet packet was delivered to wrong cpu! "
338 		 "target cpuid %d, mycpuid %d", __func__,
339 		 pkt->cpuid, mycpuid));
340 	KASSERT(pkt->ro.ro_rt == NULL,
341 		("route entry is not NULL for ip_input"));
342 	KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_IN,
343 		("wrong direction %d, should be %d",
344 		 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_IN));
345 
346 	priv = pkt->dn_priv;
347 	unref_priv = pkt->dn_unref_priv;
348 
349 	ip_input(m);
350 
351 	if (unref_priv)
352 		unref_priv(priv);
353 }
354 
355 static void
356 ip_dn_ether_demux(netmsg_t nmsg)
357 {
358 	struct netmsg_packet *nmp;
359 	struct mbuf *m;
360 	struct m_tag *mtag;
361 	struct dn_pkt *pkt;
362 	ip_dn_unref_priv_t unref_priv;
363 	void *priv;
364 
365 	nmp = &nmsg->packet;
366 	m = nmp->nm_packet;
367 	M_ASSERTPKTHDR(m);
368 	KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
369 		("mbuf is not tagged for dummynet!"));
370 
371 	mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
372 	KKASSERT(mtag != NULL);
373 
374 	pkt = m_tag_data(mtag);
375 	KASSERT(pkt->cpuid == mycpuid,
376 		("%s: dummynet packet was delivered to wrong cpu! "
377 		 "target cpuid %d, mycpuid %d", __func__,
378 		 pkt->cpuid, mycpuid));
379 	KASSERT(pkt->ro.ro_rt == NULL,
380 		("route entry is not NULL for ether_demux"));
381 	KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_DEMUX,
382 		("wrong direction %d, should be %d",
383 		 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_DEMUX));
384 
385 	priv = pkt->dn_priv;
386 	unref_priv = pkt->dn_unref_priv;
387 
388 	/*
389 	 * Make sure that ether header is contiguous
390 	 */
391 	if (m->m_len < ETHER_HDR_LEN &&
392 	    (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) {
393 		kprintf("%s: pullup fail, dropping pkt\n", __func__);
394 		goto back;
395 	}
396 	ether_demux_oncpu(m->m_pkthdr.rcvif, m);
397 back:
398 	if (unref_priv)
399 		unref_priv(priv);
400 }
401 
402 static void
403 ip_dn_ether_output(netmsg_t nmsg)
404 {
405 	struct netmsg_packet *nmp;
406 	struct mbuf *m;
407 	struct m_tag *mtag;
408 	struct dn_pkt *pkt;
409 	ip_dn_unref_priv_t unref_priv;
410 	void *priv;
411 
412 	nmp = &nmsg->packet;
413 	m = nmp->nm_packet;
414 	M_ASSERTPKTHDR(m);
415 	KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
416 		("mbuf is not tagged for dummynet!"));
417 
418 	mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
419 	KKASSERT(mtag != NULL);
420 
421 	pkt = m_tag_data(mtag);
422 	KASSERT(pkt->cpuid == mycpuid,
423 		("%s: dummynet packet was delivered to wrong cpu! "
424 		 "target cpuid %d, mycpuid %d", __func__,
425 		 pkt->cpuid, mycpuid));
426 	KASSERT(pkt->ro.ro_rt == NULL,
427 		("route entry is not NULL for ether_output_frame"));
428 	KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_OUT,
429 		("wrong direction %d, should be %d",
430 		 (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_OUT));
431 
432 	priv = pkt->dn_priv;
433 	unref_priv = pkt->dn_unref_priv;
434 
435 	ether_output_frame(pkt->ifp, m);
436 
437 	if (unref_priv)
438 		unref_priv(priv);
439 }
440 
441 static void
442 ip_dn_sockopt_dispatch(netmsg_t nmsg)
443 {
444 	lwkt_msg *msg = &nmsg->lmsg;
445 	struct dn_sopt *dn_sopt = msg->u.ms_resultp;
446 	int error;
447 
448 	KASSERT(ip_dn_cpu == mycpuid,
449 		("%s: dummynet sockopt is done on wrong cpu! "
450 		 "dummynet cpuid %d, mycpuid %d", __func__,
451 		 ip_dn_cpu, mycpuid));
452 
453 	if (DUMMYNET_LOADED)
454 		error = ip_dn_ctl_ptr(dn_sopt);
455 	else
456 		error = ENOPROTOOPT;
457 	lwkt_replymsg(msg, error);
458 }
459 
460 static int
461 ip_dn_sockopt_flush(struct sockopt *sopt)
462 {
463 	struct dn_sopt dn_sopt;
464 	struct netmsg_base smsg;
465 
466 	bzero(&dn_sopt, sizeof(dn_sopt));
467 	dn_sopt.dn_sopt_name = sopt->sopt_name;
468 
469 	netmsg_init(&smsg, NULL, &curthread->td_msgport,
470 		    0, ip_dn_sockopt_dispatch);
471 	smsg.lmsg.u.ms_resultp = &dn_sopt;
472 	lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0);
473 
474 	return smsg.lmsg.ms_error;
475 }
476 
477 static int
478 ip_dn_sockopt_get(struct sockopt *sopt)
479 {
480 	struct dn_sopt dn_sopt;
481 	struct netmsg_base smsg;
482 	int error;
483 
484 	bzero(&dn_sopt, sizeof(dn_sopt));
485 	dn_sopt.dn_sopt_name = sopt->sopt_name;
486 
487 	netmsg_init(&smsg, NULL, &curthread->td_msgport,
488 		    0, ip_dn_sockopt_dispatch);
489 	smsg.lmsg.u.ms_resultp = &dn_sopt;
490 	lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0);
491 
492 	error = smsg.lmsg.ms_error;
493 	if (error) {
494 		KKASSERT(dn_sopt.dn_sopt_arg == NULL);
495 		KKASSERT(dn_sopt.dn_sopt_arglen == 0);
496 		return error;
497 	}
498 
499 	soopt_from_kbuf(sopt, dn_sopt.dn_sopt_arg, dn_sopt.dn_sopt_arglen);
500 	kfree(dn_sopt.dn_sopt_arg, M_TEMP);
501 	return 0;
502 }
503 
504 static int
505 ip_dn_sockopt_config(struct sockopt *sopt)
506 {
507 	struct dn_ioc_pipe tmp_ioc_pipe;
508 	struct dn_sopt dn_sopt;
509 	struct netmsg_base smsg;
510 	int error;
511 
512 	error = soopt_to_kbuf(sopt, &tmp_ioc_pipe, sizeof tmp_ioc_pipe,
513 			      sizeof tmp_ioc_pipe);
514 	if (error)
515 		return error;
516 
517 	bzero(&dn_sopt, sizeof(dn_sopt));
518 	dn_sopt.dn_sopt_name = sopt->sopt_name;
519 	dn_sopt.dn_sopt_arg = &tmp_ioc_pipe;
520 	dn_sopt.dn_sopt_arglen = sizeof(tmp_ioc_pipe);
521 
522 	netmsg_init(&smsg, NULL, &curthread->td_msgport,
523 		    0, ip_dn_sockopt_dispatch);
524 	smsg.lmsg.u.ms_resultp = &dn_sopt;
525 	lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0);
526 
527 	return smsg.lmsg.ms_error;
528 }
529