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