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