xref: /openbsd-src/sys/net/if_pair.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: if_pair.c,v 1.7 2016/04/13 11:41:15 mpi 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 pair_media_change(struct ifnet *ifp)
58 {
59 	return (0);
60 }
61 
62 void
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
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
100 pairattach(int npair)
101 {
102 	if_clone_attach(&pair_cloner);
103 }
104 
105 int
106 pair_clone_create(struct if_clone *ifc, int unit)
107 {
108 	struct ifnet		*ifp;
109 	struct pair_softc	*sc;
110 
111 	if ((sc = malloc(sizeof(*sc),
112 	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
113 		return (ENOMEM);
114 
115 	ifp = &sc->sc_ac.ac_if;
116 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "pair%d", unit);
117 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
118 	ether_fakeaddr(ifp);
119 
120 	ifp->if_softc = sc;
121 	ifp->if_ioctl = pairioctl;
122 	ifp->if_start = pairstart;
123 	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
124 
125 	ifp->if_hardmtu = 0xffff;
126 	ifp->if_capabilities = IFCAP_VLAN_MTU;
127 
128 	ifmedia_init(&sc->sc_media, 0, pair_media_change,
129 	    pair_media_status);
130 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
131 	ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
132 
133 	if_attach(ifp);
134 	ether_ifattach(ifp);
135 
136 	pair_link_state(ifp);
137 
138 	return (0);
139 }
140 
141 int
142 pair_clone_destroy(struct ifnet *ifp)
143 {
144 	struct pair_softc	*sc = ifp->if_softc;
145 	struct ifnet		*pairedifp;
146 	struct pair_softc	*dstsc = ifp->if_softc;
147 
148 	if ((pairedifp = if_get(sc->sc_pairedif)) != NULL) {
149 		dstsc = pairedifp->if_softc;
150 		dstsc->sc_pairedif = 0;
151 		pair_link_state(pairedifp);
152 		if_put(pairedifp);
153 	}
154 
155 	ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
156 	ether_ifdetach(ifp);
157 	if_detach(ifp);
158 	free(sc, M_DEVBUF, sizeof(*sc));
159 
160 	return (0);
161 }
162 
163 void
164 pairstart(struct ifnet *ifp)
165 {
166 	struct pair_softc	*sc = (struct pair_softc *)ifp->if_softc;
167 	struct mbuf_list	 ml = MBUF_LIST_INITIALIZER();
168 	struct ifnet		*pairedifp;
169 	struct mbuf		*m;
170 
171 	pairedifp = if_get(sc->sc_pairedif);
172 
173 	for (;;) {
174 		IFQ_DEQUEUE(&ifp->if_snd, m);
175 		if (m == NULL)
176 			break;
177 
178 #if NBPFILTER > 0
179 		if (ifp->if_bpf)
180 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
181 #endif /* NBPFILTER > 0 */
182 
183 		ifp->if_opackets++;
184 		if (pairedifp != NULL) {
185 			if (m->m_flags & M_PKTHDR)
186 				m_resethdr(m);
187 			ml_enqueue(&ml, m);
188 		} else
189 			m_freem(m);
190 	}
191 
192 	if (pairedifp != NULL) {
193 		if_input(pairedifp, &ml);
194 		if_put(pairedifp);
195 	}
196 }
197 
198 int
199 pairioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
200 {
201 	struct pair_softc	*sc = (struct pair_softc *)ifp->if_softc;
202 	struct ifreq		*ifr = (struct ifreq *)data;
203 	struct if_clone		*ifc;
204 	struct pair_softc	*pairedsc = ifp->if_softc;
205 	struct ifnet		*oldifp = NULL, *newifp = NULL;
206 	int			 error = 0, unit;
207 
208 	switch (cmd) {
209 	case SIOCSIFADDR:
210 		ifp->if_flags |= IFF_UP;
211 		/* FALLTHROUGH */
212 
213 	case SIOCSIFFLAGS:
214 		if (ifp->if_flags & IFF_UP)
215 			ifp->if_flags |= IFF_RUNNING;
216 		else
217 			ifp->if_flags &= ~IFF_RUNNING;
218 		break;
219 
220 	case SIOCADDMULTI:
221 	case SIOCDELMULTI:
222 		break;
223 
224 	case SIOCGIFMEDIA:
225 	case SIOCSIFMEDIA:
226 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
227 		break;
228 
229 	case SIOCSIFPAIR:
230 		if (sc->sc_pairedif == ifr->ifr_index)
231 			break;
232 
233 		/* Cannot link to myself */
234 		if (ifr->ifr_index == ifp->if_index) {
235 			error = EINVAL;
236 			break;
237 		}
238 
239 		oldifp = if_get(sc->sc_pairedif);
240 		newifp = if_get(ifr->ifr_index);
241 
242 		if (newifp != NULL) {
243 			pairedsc = newifp->if_softc;
244 
245 			if (pairedsc->sc_pairedif != 0) {
246 				error = EBUSY;
247 				break;
248 			}
249 
250 			/* Only allow pair(4) interfaces for the pair */
251 			if ((ifc = if_clone_lookup(newifp->if_xname,
252 			    &unit)) == NULL || strcmp("pair",
253 			    ifc->ifc_name) != 0) {
254 				error = ENODEV;
255 				break;
256 			}
257 
258 			pairedsc->sc_pairedif = ifp->if_index;
259 			sc->sc_pairedif = ifr->ifr_index;
260 		} else
261 			sc->sc_pairedif = 0;
262 
263 		if (oldifp != NULL) {
264 			pairedsc = oldifp->if_softc;
265 			pairedsc->sc_pairedif = 0;
266 		}
267 		break;
268 
269 	case SIOCGIFPAIR:
270 		ifr->ifr_index = sc->sc_pairedif;
271 		break;
272 
273 	default:
274 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
275 	}
276 
277 	if (newifp != NULL || oldifp != NULL)
278 		pair_link_state(ifp);
279 	if (oldifp != NULL) {
280 		pair_link_state(oldifp);
281 		if_put(oldifp);
282 	}
283 	if (newifp != NULL) {
284 		pair_link_state(newifp);
285 		if_put(newifp);
286 	}
287 
288 	return (error);
289 }
290