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