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