1 /* $OpenBSD: if_pair.c,v 1.17 2021/01/13 01:57:31 kn 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 ifqueue *);
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
pair_media_change(struct ifnet * ifp)57 pair_media_change(struct ifnet *ifp)
58 {
59 return (0);
60 }
61
62 void
pair_media_status(struct ifnet * ifp,struct ifmediareq * imr)63 pair_media_status(struct ifnet *ifp, struct ifmediareq *imr)
64 {
65 struct pair_softc *sc = ifp->if_softc;
66 struct ifnet *pairedifp;
67
68 imr->ifm_active = IFM_ETHER | IFM_AUTO;
69
70 if ((pairedifp = if_get(sc->sc_pairedif)) == NULL) {
71 imr->ifm_status = 0;
72 return;
73 }
74 if_put(pairedifp);
75
76 imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
77 }
78
79 void
pair_link_state(struct ifnet * ifp)80 pair_link_state(struct ifnet *ifp)
81 {
82 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 if ((pairedifp = if_get(sc->sc_pairedif)) != NULL) {
88 link_state = LINK_STATE_UP;
89 if_put(pairedifp);
90 } else
91 link_state = LINK_STATE_DOWN;
92
93 if (ifp->if_link_state != link_state) {
94 ifp->if_link_state = link_state;
95 if_link_state_change(ifp);
96 }
97 }
98
99 void
pairattach(int npair)100 pairattach(int npair)
101 {
102 if_clone_attach(&pair_cloner);
103 }
104
105 int
pair_clone_create(struct if_clone * ifc,int unit)106 pair_clone_create(struct if_clone *ifc, int unit)
107 {
108 struct ifnet *ifp;
109 struct pair_softc *sc;
110
111 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
112 ifp = &sc->sc_ac.ac_if;
113 snprintf(ifp->if_xname, sizeof ifp->if_xname, "pair%d", unit);
114 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
115 ether_fakeaddr(ifp);
116
117 ifp->if_softc = sc;
118 ifp->if_ioctl = pairioctl;
119 ifp->if_qstart = pairstart;
120 ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE;
121
122 ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
123 ifp->if_capabilities = IFCAP_VLAN_MTU;
124
125 ifmedia_init(&sc->sc_media, 0, pair_media_change,
126 pair_media_status);
127 ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
128 ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
129
130 if_attach(ifp);
131 ether_ifattach(ifp);
132
133 pair_link_state(ifp);
134
135 return (0);
136 }
137
138 int
pair_clone_destroy(struct ifnet * ifp)139 pair_clone_destroy(struct ifnet *ifp)
140 {
141 struct pair_softc *sc = ifp->if_softc;
142 struct ifnet *pairedifp;
143 struct pair_softc *dstsc = ifp->if_softc;
144
145 if ((pairedifp = if_get(sc->sc_pairedif)) != NULL) {
146 dstsc = pairedifp->if_softc;
147 dstsc->sc_pairedif = 0;
148 pair_link_state(pairedifp);
149 if_put(pairedifp);
150 }
151
152 ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
153 ether_ifdetach(ifp);
154 if_detach(ifp);
155 free(sc, M_DEVBUF, sizeof(*sc));
156
157 return (0);
158 }
159
160 void
pairstart(struct ifqueue * ifq)161 pairstart(struct ifqueue *ifq)
162 {
163 struct ifnet *ifp = ifq->ifq_if;
164 struct pair_softc *sc = (struct pair_softc *)ifp->if_softc;
165 struct mbuf_list ml = MBUF_LIST_INITIALIZER();
166 struct ifnet *pairedifp;
167 struct mbuf *m;
168
169 pairedifp = if_get(sc->sc_pairedif);
170
171 while ((m = ifq_dequeue(ifq)) != NULL) {
172 #if NBPFILTER > 0
173 if (ifp->if_bpf)
174 bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
175 #endif /* NBPFILTER > 0 */
176
177 if (pairedifp != NULL) {
178 if (m->m_flags & M_PKTHDR)
179 m_resethdr(m);
180 ml_enqueue(&ml, m);
181 } else
182 m_freem(m);
183 }
184
185 if (pairedifp != NULL) {
186 if_input(pairedifp, &ml);
187 if_put(pairedifp);
188 }
189 }
190
191 int
pairioctl(struct ifnet * ifp,u_long cmd,caddr_t data)192 pairioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
193 {
194 struct pair_softc *sc = (struct pair_softc *)ifp->if_softc;
195 struct ifreq *ifr = (struct ifreq *)data;
196 struct if_clone *ifc;
197 struct pair_softc *pairedsc = ifp->if_softc;
198 struct ifnet *oldifp = NULL, *newifp = NULL;
199 int error = 0, unit;
200
201 switch (cmd) {
202 case SIOCSIFADDR:
203 ifp->if_flags |= IFF_UP;
204 /* FALLTHROUGH */
205
206 case SIOCSIFFLAGS:
207 if (ifp->if_flags & IFF_UP)
208 ifp->if_flags |= IFF_RUNNING;
209 else
210 ifp->if_flags &= ~IFF_RUNNING;
211 break;
212
213 case SIOCADDMULTI:
214 case SIOCDELMULTI:
215 break;
216
217 case SIOCGIFMEDIA:
218 case SIOCSIFMEDIA:
219 error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
220 break;
221
222 case SIOCSIFPAIR:
223 if (sc->sc_pairedif == ifr->ifr_index)
224 break;
225
226 /* Cannot link to myself */
227 if (ifr->ifr_index == ifp->if_index) {
228 error = EINVAL;
229 break;
230 }
231
232 oldifp = if_get(sc->sc_pairedif);
233 newifp = if_get(ifr->ifr_index);
234
235 if (newifp != NULL) {
236 pairedsc = newifp->if_softc;
237
238 if (pairedsc->sc_pairedif != 0) {
239 error = EBUSY;
240 break;
241 }
242
243 /* Only allow pair(4) interfaces for the pair */
244 if ((ifc = if_clone_lookup(newifp->if_xname,
245 &unit)) == NULL || strcmp("pair",
246 ifc->ifc_name) != 0) {
247 error = ENODEV;
248 break;
249 }
250
251 pairedsc->sc_pairedif = ifp->if_index;
252 sc->sc_pairedif = ifr->ifr_index;
253 } else
254 sc->sc_pairedif = 0;
255
256 if (oldifp != NULL) {
257 pairedsc = oldifp->if_softc;
258 pairedsc->sc_pairedif = 0;
259 }
260 break;
261
262 case SIOCGIFPAIR:
263 ifr->ifr_index = sc->sc_pairedif;
264 break;
265
266 default:
267 error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
268 }
269
270 if (newifp != NULL || oldifp != NULL)
271 pair_link_state(ifp);
272 if (oldifp != NULL) {
273 pair_link_state(oldifp);
274 if_put(oldifp);
275 }
276 if (newifp != NULL) {
277 pair_link_state(newifp);
278 if_put(newifp);
279 }
280
281 return (error);
282 }
283