Line data Source code
1 : /* $OpenBSD: if_pair.c,v 1.11 2018/01/09 15:24:24 bluhm Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
5 : * Copyright (c) 2009 Theo de Raadt <deraadt@openbsd.org>
6 : *
7 : * Permission to use, copy, modify, and distribute this software for any
8 : * purpose with or without fee is hereby granted, provided that the above
9 : * copyright notice and this permission notice appear in all copies.
10 : *
11 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 : */
19 :
20 : #include <sys/param.h>
21 : #include <sys/systm.h>
22 : #include <sys/mbuf.h>
23 : #include <sys/socket.h>
24 : #include <sys/sockio.h>
25 : #include <sys/ioctl.h>
26 :
27 : #include <net/if.h>
28 : #include <net/if_media.h>
29 :
30 : #include <netinet/in.h>
31 : #include <netinet/if_ether.h>
32 :
33 : #include "bpfilter.h"
34 : #if NBPFILTER > 0
35 : #include <net/bpf.h>
36 : #endif
37 :
38 : void pairattach(int);
39 : int pairioctl(struct ifnet *, u_long, caddr_t);
40 : void pairstart(struct ifnet *);
41 : int pair_clone_create(struct if_clone *, int);
42 : int pair_clone_destroy(struct ifnet *);
43 : int pair_media_change(struct ifnet *);
44 : void pair_media_status(struct ifnet *, struct ifmediareq *);
45 : void pair_link_state(struct ifnet *);
46 :
47 : struct pair_softc {
48 : struct arpcom sc_ac;
49 : struct ifmedia sc_media;
50 : unsigned int sc_pairedif;
51 : };
52 :
53 : struct if_clone pair_cloner =
54 : IF_CLONE_INITIALIZER("pair", pair_clone_create, pair_clone_destroy);
55 :
56 : int
57 0 : pair_media_change(struct ifnet *ifp)
58 : {
59 0 : return (0);
60 : }
61 :
62 : void
63 0 : pair_media_status(struct ifnet *ifp, struct ifmediareq *imr)
64 : {
65 0 : struct pair_softc *sc = ifp->if_softc;
66 : struct ifnet *pairedifp;
67 :
68 0 : imr->ifm_active = IFM_ETHER | IFM_AUTO;
69 :
70 0 : if ((pairedifp = if_get(sc->sc_pairedif)) == NULL) {
71 0 : imr->ifm_status = 0;
72 0 : return;
73 : }
74 0 : if_put(pairedifp);
75 :
76 0 : imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
77 0 : }
78 :
79 : void
80 0 : pair_link_state(struct ifnet *ifp)
81 : {
82 0 : struct pair_softc *sc = ifp->if_softc;
83 : struct ifnet *pairedifp;
84 : unsigned int link_state;
85 :
86 : /* The pair state is determined by the paired interface */
87 0 : if ((pairedifp = if_get(sc->sc_pairedif)) != NULL) {
88 : link_state = LINK_STATE_UP;
89 0 : if_put(pairedifp);
90 0 : } else
91 : link_state = LINK_STATE_DOWN;
92 :
93 0 : if (ifp->if_link_state != link_state) {
94 0 : ifp->if_link_state = link_state;
95 0 : if_link_state_change(ifp);
96 0 : }
97 0 : }
98 :
99 : void
100 0 : pairattach(int npair)
101 : {
102 0 : if_clone_attach(&pair_cloner);
103 0 : }
104 :
105 : int
106 0 : pair_clone_create(struct if_clone *ifc, int unit)
107 : {
108 : struct ifnet *ifp;
109 : struct pair_softc *sc;
110 :
111 0 : sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
112 0 : ifp = &sc->sc_ac.ac_if;
113 0 : snprintf(ifp->if_xname, sizeof ifp->if_xname, "pair%d", unit);
114 0 : ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
115 0 : ether_fakeaddr(ifp);
116 :
117 0 : ifp->if_softc = sc;
118 0 : ifp->if_ioctl = pairioctl;
119 0 : ifp->if_start = pairstart;
120 0 : ifp->if_xflags = IFXF_CLONED;
121 0 : IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
122 :
123 0 : ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
124 0 : ifp->if_capabilities = IFCAP_VLAN_MTU;
125 :
126 0 : ifmedia_init(&sc->sc_media, 0, pair_media_change,
127 : pair_media_status);
128 0 : ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
129 0 : ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
130 :
131 0 : if_attach(ifp);
132 0 : ether_ifattach(ifp);
133 :
134 0 : pair_link_state(ifp);
135 :
136 0 : return (0);
137 : }
138 :
139 : int
140 0 : pair_clone_destroy(struct ifnet *ifp)
141 : {
142 0 : struct pair_softc *sc = ifp->if_softc;
143 : struct ifnet *pairedifp;
144 : struct pair_softc *dstsc = ifp->if_softc;
145 :
146 0 : if ((pairedifp = if_get(sc->sc_pairedif)) != NULL) {
147 0 : dstsc = pairedifp->if_softc;
148 0 : dstsc->sc_pairedif = 0;
149 0 : pair_link_state(pairedifp);
150 0 : if_put(pairedifp);
151 0 : }
152 :
153 0 : ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
154 0 : ether_ifdetach(ifp);
155 0 : if_detach(ifp);
156 0 : free(sc, M_DEVBUF, sizeof(*sc));
157 :
158 0 : return (0);
159 : }
160 :
161 : void
162 0 : pairstart(struct ifnet *ifp)
163 : {
164 0 : struct pair_softc *sc = (struct pair_softc *)ifp->if_softc;
165 0 : struct mbuf_list ml = MBUF_LIST_INITIALIZER();
166 : struct ifnet *pairedifp;
167 : struct mbuf *m;
168 :
169 0 : pairedifp = if_get(sc->sc_pairedif);
170 :
171 0 : for (;;) {
172 0 : IFQ_DEQUEUE(&ifp->if_snd, m);
173 0 : if (m == NULL)
174 : break;
175 :
176 : #if NBPFILTER > 0
177 0 : if (ifp->if_bpf)
178 0 : bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
179 : #endif /* NBPFILTER > 0 */
180 :
181 0 : if (pairedifp != NULL) {
182 0 : if (m->m_flags & M_PKTHDR)
183 0 : m_resethdr(m);
184 0 : ml_enqueue(&ml, m);
185 0 : } else
186 0 : m_freem(m);
187 : }
188 :
189 0 : if (pairedifp != NULL) {
190 0 : if_input(pairedifp, &ml);
191 0 : if_put(pairedifp);
192 0 : }
193 0 : }
194 :
195 : int
196 0 : pairioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
197 : {
198 0 : struct pair_softc *sc = (struct pair_softc *)ifp->if_softc;
199 0 : struct ifreq *ifr = (struct ifreq *)data;
200 : struct if_clone *ifc;
201 : struct pair_softc *pairedsc = ifp->if_softc;
202 : struct ifnet *oldifp = NULL, *newifp = NULL;
203 0 : int error = 0, unit;
204 :
205 0 : switch (cmd) {
206 : case SIOCSIFADDR:
207 0 : ifp->if_flags |= IFF_UP;
208 : /* FALLTHROUGH */
209 :
210 : case SIOCSIFFLAGS:
211 0 : if (ifp->if_flags & IFF_UP)
212 0 : ifp->if_flags |= IFF_RUNNING;
213 : else
214 0 : ifp->if_flags &= ~IFF_RUNNING;
215 : break;
216 :
217 : case SIOCADDMULTI:
218 : case SIOCDELMULTI:
219 : break;
220 :
221 : case SIOCGIFMEDIA:
222 : case SIOCSIFMEDIA:
223 0 : error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
224 0 : break;
225 :
226 : case SIOCSIFPAIR:
227 0 : if (sc->sc_pairedif == ifr->ifr_index)
228 : break;
229 :
230 : /* Cannot link to myself */
231 0 : if (ifr->ifr_index == ifp->if_index) {
232 : error = EINVAL;
233 0 : break;
234 : }
235 :
236 0 : oldifp = if_get(sc->sc_pairedif);
237 0 : newifp = if_get(ifr->ifr_index);
238 :
239 0 : if (newifp != NULL) {
240 0 : pairedsc = newifp->if_softc;
241 :
242 0 : if (pairedsc->sc_pairedif != 0) {
243 : error = EBUSY;
244 0 : break;
245 : }
246 :
247 : /* Only allow pair(4) interfaces for the pair */
248 0 : if ((ifc = if_clone_lookup(newifp->if_xname,
249 0 : &unit)) == NULL || strcmp("pair",
250 0 : ifc->ifc_name) != 0) {
251 : error = ENODEV;
252 0 : break;
253 : }
254 :
255 0 : pairedsc->sc_pairedif = ifp->if_index;
256 0 : sc->sc_pairedif = ifr->ifr_index;
257 0 : } else
258 0 : sc->sc_pairedif = 0;
259 :
260 0 : if (oldifp != NULL) {
261 0 : pairedsc = oldifp->if_softc;
262 0 : pairedsc->sc_pairedif = 0;
263 0 : }
264 : break;
265 :
266 : case SIOCGIFPAIR:
267 0 : ifr->ifr_index = sc->sc_pairedif;
268 0 : break;
269 :
270 : default:
271 0 : error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
272 0 : }
273 :
274 0 : if (newifp != NULL || oldifp != NULL)
275 0 : pair_link_state(ifp);
276 0 : if (oldifp != NULL) {
277 0 : pair_link_state(oldifp);
278 0 : if_put(oldifp);
279 0 : }
280 0 : if (newifp != NULL) {
281 0 : pair_link_state(newifp);
282 0 : if_put(newifp);
283 0 : }
284 :
285 0 : return (error);
286 0 : }
|