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