Line data Source code
1 : /* $OpenBSD: switchctl.c,v 1.12 2017/08/11 21:24:19 mpi Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2016 Kazuya GODA <goda@openbsd.org>
5 : * Copyright (c) 2015, 2016 YASUOKA Masahiko <yasuoka@openbsd.org>
6 : * Copyright (c) 2015, 2016 Reyk Floeter <reyk@openbsd.org>
7 : *
8 : * Permission to use, copy, modify, and distribute this software for any
9 : * purpose with or without fee is hereby granted, provided that the above
10 : * copyright notice and this permission notice appear in all copies.
11 : *
12 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 : */
20 :
21 : #include <sys/param.h>
22 : #include <sys/systm.h>
23 : #include <sys/mbuf.h>
24 : #include <sys/socket.h>
25 : #include <sys/ioctl.h>
26 : #include <sys/selinfo.h>
27 : #include <sys/rwlock.h>
28 : #include <sys/proc.h>
29 :
30 : #include <net/if.h>
31 : #include <net/rtable.h>
32 :
33 : #include <netinet/in.h>
34 : #include <netinet/if_ether.h>
35 :
36 : #include <net/if_switch.h>
37 :
38 : extern struct rwlock switch_ifs_lk;
39 :
40 : /*
41 : * device part of switch(4)
42 : */
43 : #include <sys/poll.h>
44 : #include <sys/selinfo.h>
45 : #include <sys/vnode.h>
46 :
47 : struct switch_softc *switch_dev2sc(dev_t);
48 : int switchopen(dev_t, int, int, struct proc *);
49 : int switchread(dev_t, struct uio *, int);
50 : int switchwrite(dev_t, struct uio *, int);
51 : int switchioctl(dev_t, u_long, caddr_t, int, struct proc *);
52 : int switchclose(dev_t, int, int, struct proc *);
53 : int switchpoll(dev_t, int, struct proc *);
54 : int switchkqfilter(dev_t, struct knote *);
55 : void filt_switch_rdetach(struct knote *);
56 : int filt_switch_read(struct knote *, long);
57 : void filt_switch_wdetach(struct knote *);
58 : int filt_switch_write(struct knote *, long);
59 : int switch_dev_output(struct switch_softc *, struct mbuf *);
60 : void switch_dev_wakeup(struct switch_softc *);
61 :
62 : struct filterops switch_rd_filtops = {
63 : 1, NULL, filt_switch_rdetach, filt_switch_read
64 : };
65 : struct filterops switch_wr_filtops = {
66 : 1, NULL, filt_switch_wdetach, filt_switch_write
67 : };
68 :
69 : struct switch_softc *
70 0 : switch_dev2sc(dev_t dev)
71 : {
72 : struct switch_softc *sc;
73 :
74 0 : rw_enter_read(&switch_ifs_lk);
75 0 : sc = switch_lookup(minor(dev));
76 0 : rw_exit_read(&switch_ifs_lk);
77 :
78 0 : return (sc);
79 : }
80 :
81 : int
82 0 : switchopen(dev_t dev, int flags, int mode, struct proc *p)
83 : {
84 : struct switch_softc *sc;
85 0 : char name[IFNAMSIZ];
86 : int rv, s, error = 0;
87 0 : unsigned int rdomain = rtable_l2(p->p_p->ps_rtableid);
88 :
89 0 : if ((sc = switch_dev2sc(dev)) == NULL) {
90 0 : snprintf(name, sizeof(name), "switch%d", minor(dev));
91 0 : NET_LOCK();
92 0 : rv = if_clone_create(name, rdomain);
93 0 : NET_UNLOCK();
94 0 : if (rv != 0)
95 0 : return (rv);
96 0 : if ((sc = switch_dev2sc(dev)) == NULL)
97 0 : return (ENXIO);
98 : }
99 :
100 0 : rw_enter_write(&switch_ifs_lk);
101 0 : if (sc->sc_swdev != NULL) {
102 : error = EBUSY;
103 0 : goto failed;
104 : }
105 :
106 0 : if ((sc->sc_swdev = malloc(sizeof(struct switch_dev), M_DEVBUF,
107 0 : M_DONTWAIT|M_ZERO)) == NULL ) {
108 : error = ENOBUFS;
109 0 : goto failed;
110 : }
111 :
112 0 : s = splnet();
113 0 : mq_init(&sc->sc_swdev->swdev_outq, 128, IPL_NET);
114 :
115 0 : sc->sc_swdev->swdev_output = switch_dev_output;
116 0 : if (sc->sc_capabilities & SWITCH_CAP_OFP)
117 0 : swofp_init(sc);
118 :
119 0 : splx(s);
120 :
121 : failed:
122 0 : rw_exit_write(&switch_ifs_lk);
123 0 : return (error);
124 :
125 0 : }
126 :
127 : int
128 0 : switchread(dev_t dev, struct uio *uio, int ioflag)
129 : {
130 : struct switch_softc *sc;
131 : struct mbuf *m;
132 : u_int len;
133 : int s, error = 0;
134 :
135 0 : sc = switch_dev2sc(dev);
136 0 : if (sc == NULL)
137 0 : return (ENXIO);
138 :
139 0 : if (sc->sc_swdev->swdev_lastm != NULL) {
140 : m = sc->sc_swdev->swdev_lastm;
141 0 : sc->sc_swdev->swdev_lastm = NULL;
142 0 : goto skip_dequeue;
143 : }
144 :
145 : dequeue_next:
146 0 : s = splnet();
147 0 : while ((m = mq_dequeue(&sc->sc_swdev->swdev_outq)) == NULL) {
148 0 : if (ISSET(ioflag, IO_NDELAY)) {
149 : error = EWOULDBLOCK;
150 0 : goto failed;
151 : }
152 0 : sc->sc_swdev->swdev_waiting = 1;
153 0 : error = tsleep(sc, (PZERO + 1)|PCATCH, "switchread", 0);
154 0 : if (error != 0)
155 : goto failed;
156 : /* sc might be deleted while sleeping */
157 0 : sc = switch_dev2sc(dev);
158 0 : if (sc == NULL) {
159 : error = ENXIO;
160 0 : goto failed;
161 : }
162 : }
163 0 : splx(s);
164 :
165 : skip_dequeue:
166 0 : while (uio->uio_resid > 0) {
167 0 : len = ulmin(uio->uio_resid, m->m_len);
168 0 : if ((error = uiomove(mtod(m, caddr_t), len, uio)) != 0) {
169 : /* Save it so user can recover from EFAULT. */
170 0 : sc->sc_swdev->swdev_lastm = m;
171 0 : return (error);
172 : }
173 :
174 : /* Handle partial reads. */
175 0 : if (uio->uio_resid == 0) {
176 0 : if (len < m->m_len)
177 0 : m_adj(m, len);
178 : else
179 0 : m = m_free(m);
180 0 : sc->sc_swdev->swdev_lastm = m;
181 0 : break;
182 : }
183 :
184 : /*
185 : * After consuming data from this mbuf test if we
186 : * have to dequeue a new chain.
187 : */
188 0 : m = m_free(m);
189 0 : if (m == NULL)
190 : goto dequeue_next;
191 : }
192 :
193 0 : return (0);
194 : failed:
195 0 : splx(s);
196 0 : return (error);
197 0 : }
198 :
199 : int
200 0 : switchwrite(dev_t dev, struct uio *uio, int ioflag)
201 : {
202 : struct switch_softc *sc = NULL;
203 0 : struct mbuf *m, *n, *mhead, *mtail = NULL;
204 : int s, error, trailing;
205 : size_t len;
206 :
207 0 : if (uio->uio_resid == 0)
208 0 : return (0);
209 :
210 : len = uio->uio_resid;
211 :
212 0 : sc = switch_dev2sc(dev);
213 0 : if (sc == NULL)
214 0 : return (ENXIO);
215 :
216 0 : if (sc->sc_swdev->swdev_inputm == NULL) {
217 0 : MGETHDR(m, M_DONTWAIT, MT_DATA);
218 0 : if (m == NULL)
219 0 : return (ENOBUFS);
220 0 : if (len >= MHLEN) {
221 0 : MCLGETI(m, M_DONTWAIT, NULL, MIN(MAXMCLBYTES, len));
222 0 : if ((m->m_flags & M_EXT) == 0) {
223 0 : m_free(m);
224 0 : return (ENOBUFS);
225 : }
226 : }
227 : mhead = m;
228 :
229 : /* M_TRAILINGSPACE() uses this to calculate space. */
230 0 : m->m_len = 0;
231 0 : } else {
232 : /* Recover the mbuf from the last write and get its tail. */
233 : mhead = sc->sc_swdev->swdev_inputm;
234 0 : for (m = mhead; m->m_next != NULL; m = m->m_next)
235 : /* NOTHING */;
236 :
237 0 : sc->sc_swdev->swdev_inputm = NULL;
238 : }
239 :
240 0 : while (len) {
241 0 : trailing = ulmin(M_TRAILINGSPACE(m), len);
242 0 : if ((error = uiomove(mtod(m, caddr_t), trailing, uio)) != 0)
243 : goto save_return;
244 :
245 0 : len -= trailing;
246 0 : mhead->m_pkthdr.len += trailing;
247 0 : m->m_len += trailing;
248 0 : if (len == 0)
249 : break;
250 :
251 0 : MGET(n, M_DONTWAIT, MT_DATA);
252 0 : if (n == NULL) {
253 : error = ENOBUFS;
254 0 : goto save_return;
255 : }
256 0 : if (len >= MLEN) {
257 0 : MCLGETI(n, M_DONTWAIT, NULL, MIN(MAXMCLBYTES, len));
258 0 : if ((n->m_flags & M_EXT) == 0) {
259 0 : m_free(n);
260 : error = ENOBUFS;
261 0 : goto save_return;
262 : }
263 : }
264 0 : n->m_len = 0;
265 :
266 0 : m->m_next = n;
267 : m = n;
268 : }
269 :
270 : /* Loop until there is no more complete OFP packets. */
271 0 : while (ofp_split_mbuf(mhead, &mtail) == 0) {
272 0 : s = splnet();
273 0 : sc->sc_swdev->swdev_input(sc, mhead);
274 0 : splx(s);
275 :
276 : /* We wrote everything, just quit. */
277 0 : if (mtail == NULL)
278 0 : return (0);
279 :
280 : mhead = mtail;
281 : }
282 :
283 : /* Save the head, because ofp_split_mbuf failed. */
284 0 : sc->sc_swdev->swdev_inputm = mhead;
285 :
286 0 : return (0);
287 :
288 : save_return:
289 : /* Save it so user can recover from errors later. */
290 0 : sc->sc_swdev->swdev_inputm = mhead;
291 0 : return (error);
292 0 : }
293 :
294 : int
295 0 : switchioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
296 : {
297 : int error;
298 :
299 0 : switch (cmd) {
300 : case FIONBIO:
301 : case FIOASYNC:
302 : case FIONREAD:
303 0 : return (0);
304 : default:
305 : error = ENOTTY;
306 : break;
307 : }
308 :
309 0 : return (error);
310 0 : }
311 :
312 : int
313 0 : switchclose(dev_t dev, int flags, int mode, struct proc *p)
314 : {
315 : struct switch_softc *sc;
316 :
317 0 : rw_enter_write(&switch_ifs_lk);
318 0 : sc = switch_lookup(minor(dev));
319 0 : if (sc != NULL && sc->sc_swdev != NULL) {
320 0 : m_freem(sc->sc_swdev->swdev_lastm);
321 0 : m_freem(sc->sc_swdev->swdev_inputm);
322 0 : mq_purge(&sc->sc_swdev->swdev_outq);
323 0 : free(sc->sc_swdev, M_DEVBUF, sizeof(struct switch_dev));
324 0 : sc->sc_swdev = NULL;
325 0 : }
326 0 : rw_exit_write(&switch_ifs_lk);
327 :
328 0 : return (0);
329 : }
330 :
331 : void
332 0 : switch_dev_destroy(struct switch_softc *sc)
333 : {
334 : int s;
335 :
336 0 : if (sc->sc_swdev == NULL)
337 0 : return;
338 0 : rw_enter_write(&switch_ifs_lk);
339 0 : if (sc->sc_swdev != NULL) {
340 0 : switch_dev_wakeup(sc);
341 :
342 0 : s = splhigh();
343 0 : klist_invalidate(&sc->sc_swdev->swdev_rsel.si_note);
344 0 : klist_invalidate(&sc->sc_swdev->swdev_wsel.si_note);
345 0 : splx(s);
346 :
347 0 : m_freem(sc->sc_swdev->swdev_lastm);
348 0 : m_freem(sc->sc_swdev->swdev_inputm);
349 0 : mq_purge(&sc->sc_swdev->swdev_outq);
350 0 : free(sc->sc_swdev, M_DEVBUF, sizeof(struct switch_dev));
351 0 : sc->sc_swdev = NULL;
352 0 : }
353 0 : rw_exit_write(&switch_ifs_lk);
354 0 : }
355 :
356 : int
357 0 : switchpoll(dev_t dev, int events, struct proc *p)
358 : {
359 : int revents = 0;
360 0 : struct switch_softc *sc = switch_dev2sc(dev);
361 :
362 0 : if (sc == NULL)
363 0 : return (ENXIO);
364 :
365 0 : if (events & (POLLIN | POLLRDNORM)) {
366 0 : if (!mq_empty(&sc->sc_swdev->swdev_outq) ||
367 0 : sc->sc_swdev->swdev_lastm != NULL)
368 0 : revents |= events & (POLLIN | POLLRDNORM);
369 : }
370 0 : if (events & (POLLOUT | POLLWRNORM))
371 0 : revents |= events & (POLLOUT | POLLWRNORM);
372 0 : if (revents == 0) {
373 0 : if (events & (POLLIN | POLLRDNORM))
374 0 : selrecord(p, &sc->sc_swdev->swdev_rsel);
375 : }
376 :
377 0 : return (revents);
378 0 : }
379 :
380 : int
381 0 : switchkqfilter(dev_t dev, struct knote *kn)
382 : {
383 0 : struct switch_softc *sc = switch_dev2sc(dev);
384 : struct klist *klist;
385 :
386 0 : if (sc == NULL)
387 0 : return (ENXIO);
388 :
389 0 : switch (kn->kn_filter) {
390 : case EVFILT_READ:
391 0 : klist = &sc->sc_swdev->swdev_rsel.si_note;
392 0 : kn->kn_fop = &switch_rd_filtops;
393 0 : break;
394 : case EVFILT_WRITE:
395 0 : klist = &sc->sc_swdev->swdev_wsel.si_note;
396 0 : kn->kn_fop = &switch_wr_filtops;
397 0 : break;
398 : default:
399 0 : return (EINVAL);
400 : }
401 :
402 0 : kn->kn_hook = (caddr_t)sc;
403 :
404 0 : SLIST_INSERT_HEAD(klist, kn, kn_selnext);
405 :
406 0 : return (0);
407 0 : }
408 :
409 : void
410 0 : filt_switch_rdetach(struct knote *kn)
411 : {
412 0 : struct switch_softc *sc = (struct switch_softc *)kn->kn_hook;
413 0 : struct klist *klist = &sc->sc_swdev->swdev_rsel.si_note;
414 :
415 0 : if (ISSET(kn->kn_status, KN_DETACHED))
416 0 : return;
417 :
418 0 : SLIST_REMOVE(klist, kn, knote, kn_selnext);
419 0 : }
420 :
421 : int
422 0 : filt_switch_read(struct knote *kn, long hint)
423 : {
424 0 : struct switch_softc *sc = (struct switch_softc *)kn->kn_hook;
425 :
426 0 : if (ISSET(kn->kn_status, KN_DETACHED)) {
427 0 : kn->kn_data = 0;
428 0 : return (1);
429 : }
430 :
431 0 : if (!mq_empty(&sc->sc_swdev->swdev_outq) ||
432 0 : sc->sc_swdev->swdev_lastm != NULL) {
433 0 : kn->kn_data = mq_len(&sc->sc_swdev->swdev_outq) +
434 0 : (sc->sc_swdev->swdev_lastm != NULL);
435 0 : return (1);
436 : }
437 :
438 0 : return (0);
439 0 : }
440 :
441 : void
442 0 : filt_switch_wdetach(struct knote *kn)
443 : {
444 0 : struct switch_softc *sc = (struct switch_softc *)kn->kn_hook;
445 0 : struct klist *klist = &sc->sc_swdev->swdev_wsel.si_note;
446 :
447 0 : if (ISSET(kn->kn_status, KN_DETACHED))
448 0 : return;
449 :
450 0 : SLIST_REMOVE(klist, kn, knote, kn_selnext);
451 0 : }
452 :
453 : int
454 0 : filt_switch_write(struct knote *kn, long hint)
455 : {
456 : /* Always writable */
457 0 : return (1);
458 : }
459 :
460 : int
461 0 : switch_dev_output(struct switch_softc *sc, struct mbuf *m)
462 : {
463 0 : if (mq_enqueue(&sc->sc_swdev->swdev_outq, m) != 0)
464 0 : return (-1);
465 0 : switch_dev_wakeup(sc);
466 :
467 0 : return (0);
468 0 : }
469 :
470 : void
471 0 : switch_dev_wakeup(struct switch_softc *sc)
472 : {
473 0 : if (sc->sc_swdev->swdev_waiting) {
474 0 : sc->sc_swdev->swdev_waiting = 0;
475 0 : wakeup((caddr_t)sc);
476 0 : }
477 0 : selwakeup(&sc->sc_swdev->swdev_rsel);
478 0 : }
|