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