Merge remote-tracking branch 'origin/vendor/GCC80'
[dragonfly.git] / sys / net / dummynet3 / ip_dummynet3_glue.c
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
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
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
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
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
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
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
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
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
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         /* ip_input() expects ip_off/ip_len in network byte order. */
353         KKASSERT(m->m_len >= sizeof(*ip));
354         ip = mtod(m, struct ip *);
355         ip->ip_len = htons(ip->ip_len);
356         ip->ip_off = htons(ip->ip_off);
357
358         ip_input(m);
359
360         if (unref_priv)
361                 unref_priv(priv);
362 }
363
364 static void
365 ip_dn_ether_demux(netmsg_t nmsg)
366 {
367         struct netmsg_packet *nmp;
368         struct mbuf *m;
369         struct m_tag *mtag;
370         struct dn_pkt *pkt;
371         ip_dn_unref_priv_t unref_priv;
372         void *priv;
373
374         nmp = &nmsg->packet;
375         m = nmp->nm_packet;
376         M_ASSERTPKTHDR(m);
377         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
378                 ("mbuf is not tagged for dummynet!"));
379
380         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
381         KKASSERT(mtag != NULL);
382
383         pkt = m_tag_data(mtag);
384         KASSERT(pkt->cpuid == mycpuid,
385                 ("%s: dummynet packet was delivered to wrong cpu! "
386                  "target cpuid %d, mycpuid %d", __func__,
387                  pkt->cpuid, mycpuid));
388         KASSERT(pkt->ro.ro_rt == NULL,
389                 ("route entry is not NULL for ether_demux"));
390         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_DEMUX,
391                 ("wrong direction %d, should be %d",
392                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_DEMUX));
393
394         priv = pkt->dn_priv;
395         unref_priv = pkt->dn_unref_priv;
396
397         /*
398          * Make sure that ether header is contiguous
399          */
400         if (m->m_len < ETHER_HDR_LEN &&
401                 (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) {
402                 kprintf("%s: pullup fail, dropping pkt\n", __func__);
403                 goto back;
404         }
405         ether_demux_oncpu(m->m_pkthdr.rcvif, m);
406 back:
407         if (unref_priv)
408                 unref_priv(priv);
409 }
410
411 static void
412 ip_dn_ether_output(netmsg_t nmsg)
413 {
414         struct netmsg_packet *nmp;
415         struct mbuf *m;
416         struct m_tag *mtag;
417         struct dn_pkt *pkt;
418         ip_dn_unref_priv_t unref_priv;
419         void *priv;
420
421         nmp = &nmsg->packet;
422         m = nmp->nm_packet;
423         M_ASSERTPKTHDR(m);
424         KASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED,
425                 ("mbuf is not tagged for dummynet!"));
426
427         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
428         KKASSERT(mtag != NULL);
429
430         pkt = m_tag_data(mtag);
431         KASSERT(pkt->cpuid == mycpuid,
432                 ("%s: dummynet packet was delivered to wrong cpu! "
433                  "target cpuid %d, mycpuid %d", __func__,
434                  pkt->cpuid, mycpuid));
435         KASSERT(pkt->ro.ro_rt == NULL,
436                 ("route entry is not NULL for ether_output_frame"));
437         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_OUT,
438                 ("wrong direction %d, should be %d",
439                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_OUT));
440
441         priv = pkt->dn_priv;
442         unref_priv = pkt->dn_unref_priv;
443
444         ether_output_frame(pkt->ifp, m);
445
446         if (unref_priv)
447                 unref_priv(priv);
448 }
449
450 static void
451 ip_dn_sockopt_dispatch(netmsg_t nmsg)
452 {
453         lwkt_msg *msg = &nmsg->lmsg;
454         struct dn_sopt *dn_sopt = msg->u.ms_resultp;
455         int error;
456
457         KASSERT(ip_dn_cpu == mycpuid,
458                 ("%s: dummynet sockopt is done on wrong cpu! "
459                  "dummynet cpuid %d, mycpuid %d", __func__,
460                  ip_dn_cpu, mycpuid));
461
462         if (DUMMYNET_LOADED)
463                 error = ip_dn_ctl_ptr(dn_sopt);
464         else
465                 error = ENOPROTOOPT;
466         lwkt_replymsg(msg, error);
467 }
468
469 static int
470 ip_dn_sockopt_flush(struct sockopt *sopt)
471 {
472         struct dn_sopt dn_sopt;
473         struct netmsg_base smsg;
474
475         bzero(&dn_sopt, sizeof(dn_sopt));
476         dn_sopt.dn_sopt_name = sopt->sopt_name;
477
478         netmsg_init(&smsg, NULL, &curthread->td_msgport,
479                         0, ip_dn_sockopt_dispatch);
480         smsg.lmsg.u.ms_resultp = &dn_sopt;
481         lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0);
482
483         return smsg.lmsg.ms_error;
484 }
485
486 static int
487 ip_dn_sockopt_get(struct sockopt *sopt)
488 {
489         struct dn_sopt dn_sopt;
490         struct netmsg_base smsg;
491         int error;
492
493         bzero(&dn_sopt, sizeof(dn_sopt));
494         dn_sopt.dn_sopt_name = sopt->sopt_name;
495
496         netmsg_init(&smsg, NULL, &curthread->td_msgport,
497                         0, ip_dn_sockopt_dispatch);
498         smsg.lmsg.u.ms_resultp = &dn_sopt;
499         lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0);
500
501         error = smsg.lmsg.ms_error;
502         if (error) {
503                 KKASSERT(dn_sopt.dn_sopt_arg == NULL);
504                 KKASSERT(dn_sopt.dn_sopt_arglen == 0);
505                 return error;
506         }
507
508         soopt_from_kbuf(sopt, dn_sopt.dn_sopt_arg, dn_sopt.dn_sopt_arglen);
509         kfree(dn_sopt.dn_sopt_arg, M_TEMP);
510         return 0;
511 }
512
513 static int
514 ip_dn_sockopt_config(struct sockopt *sopt)
515 {
516         struct dn_ioc_pipe tmp_ioc_pipe;
517         struct dn_sopt dn_sopt;
518         struct netmsg_base smsg;
519         int error;
520
521         error = soopt_to_kbuf(sopt, &tmp_ioc_pipe, sizeof tmp_ioc_pipe,
522                                   sizeof tmp_ioc_pipe);
523         if (error)
524                 return error;
525
526         bzero(&dn_sopt, sizeof(dn_sopt));
527         dn_sopt.dn_sopt_name = sopt->sopt_name;
528         dn_sopt.dn_sopt_arg = &tmp_ioc_pipe;
529         dn_sopt.dn_sopt_arglen = sizeof(tmp_ioc_pipe);
530
531         netmsg_init(&smsg, NULL, &curthread->td_msgport,
532                         0, ip_dn_sockopt_dispatch);
533         smsg.lmsg.u.ms_resultp = &dn_sopt;
534         lwkt_domsg(netisr_cpuport(ip_dn_cpu), &smsg.lmsg, 0);
535
536         return smsg.lmsg.ms_error;
537 }