xref: /openbsd-src/sys/net/if_enc.c (revision 18a44669273a480c6bde7a39e70e368af32c272f)
1*18a44669Sbluhm /*	$OpenBSD: if_enc.c,v 1.79 2022/08/29 07:51:45 bluhm Exp $	*/
245afdb40Sderaadt 
345afdb40Sderaadt /*
48ddcae73Sreyk  * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
58ddcae73Sreyk  *
68ddcae73Sreyk  * Permission to use, copy, modify, and distribute this software for any
78ddcae73Sreyk  * purpose with or without fee is hereby granted, provided that the above
88ddcae73Sreyk  * copyright notice and this permission notice appear in all copies.
98ddcae73Sreyk  *
108ddcae73Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
118ddcae73Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
128ddcae73Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
138ddcae73Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
148ddcae73Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158ddcae73Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168ddcae73Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1745afdb40Sderaadt  */
1845afdb40Sderaadt 
198ddcae73Sreyk #include "bpfilter.h"
208ddcae73Sreyk 
2145afdb40Sderaadt #include <sys/param.h>
2245afdb40Sderaadt #include <sys/systm.h>
238ddcae73Sreyk #include <sys/malloc.h>
2445afdb40Sderaadt #include <sys/socket.h>
258ddcae73Sreyk #include <sys/sockio.h>
268ddcae73Sreyk #include <sys/mbuf.h>
27c826b1e3Sespie 
2845afdb40Sderaadt #include <net/if.h>
29c7b7b779Sbluhm #include <net/if_dl.h>
300deb6685Smpi #include <net/if_var.h>
318ddcae73Sreyk #include <net/if_enc.h>
3245afdb40Sderaadt #include <net/if_types.h>
338ddcae73Sreyk #if NBPFILTER > 0
3445afdb40Sderaadt #include <net/bpf.h>
3545afdb40Sderaadt #endif
3645afdb40Sderaadt 
378ddcae73Sreyk struct ifnet			**enc_ifps;	/* rdomain-mapped enc ifs */
38e1bf84faSjca u_int				  enc_max_rdomain;
39a43d4d9bSreyk struct ifnet			**enc_allifps;	/* unit-mapped enc ifs */
40a43d4d9bSreyk u_int				  enc_max_unit;
41a43d4d9bSreyk #define ENC_MAX_UNITS		  4096		/* XXX n per rdomain */
4245afdb40Sderaadt 
43c4071fd1Smillert void	 encattach(int);
448ddcae73Sreyk 
458ddcae73Sreyk int	 enc_clone_create(struct if_clone *, int);
468ddcae73Sreyk int	 enc_clone_destroy(struct ifnet *);
478ddcae73Sreyk int	 enc_output(struct ifnet *, struct mbuf *, struct sockaddr *,
48c4071fd1Smillert 	    struct rtentry *);
498ddcae73Sreyk int	 enc_ioctl(struct ifnet *, u_long, caddr_t);
508ddcae73Sreyk 
518ddcae73Sreyk int	 enc_setif(struct ifnet *, u_int);
528ddcae73Sreyk void	 enc_unsetif(struct ifnet *);
538ddcae73Sreyk 
548ddcae73Sreyk struct if_clone enc_cloner =
558ddcae73Sreyk     IF_CLONE_INITIALIZER("enc", enc_clone_create, enc_clone_destroy);
56443a876bSangelos 
5745afdb40Sderaadt void
encattach(int count)588ddcae73Sreyk encattach(int count)
5945afdb40Sderaadt {
608ddcae73Sreyk 	/* Create enc0 by default */
618ddcae73Sreyk 	(void)enc_clone_create(&enc_cloner, 0);
628ddcae73Sreyk 
638ddcae73Sreyk 	if_clone_attach(&enc_cloner);
648ddcae73Sreyk }
658ddcae73Sreyk 
668ddcae73Sreyk int
enc_clone_create(struct if_clone * ifc,int unit)678ddcae73Sreyk enc_clone_create(struct if_clone *ifc, int unit)
688ddcae73Sreyk {
698ddcae73Sreyk 	struct enc_softc	*sc;
708ddcae73Sreyk 	struct ifnet		*ifp;
71a43d4d9bSreyk 	struct ifnet		**new;
728a828045Sflorian 	size_t			 oldlen;
73aa28b9a6Smpi 	int			 error;
74a43d4d9bSreyk 
75a43d4d9bSreyk 	if (unit > ENC_MAX_UNITS)
76a43d4d9bSreyk 		return (EINVAL);
778ddcae73Sreyk 
78210de69cSmpi 	if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
79ec93e248Sbluhm 		return (ENOBUFS);
808ddcae73Sreyk 
818ddcae73Sreyk 	sc->sc_unit = unit;
828ddcae73Sreyk 
838ddcae73Sreyk 	ifp = &sc->sc_if;
848ddcae73Sreyk 	ifp->if_softc = sc;
85443a876bSangelos 	ifp->if_type = IFT_ENC;
8699bf4b5fSmpi 	ifp->if_xflags = IFXF_CLONED;
878ddcae73Sreyk 	ifp->if_output = enc_output;
888ddcae73Sreyk 	ifp->if_ioctl = enc_ioctl;
89443a876bSangelos 	ifp->if_hdrlen = ENC_HDRLEN;
908ddcae73Sreyk 
918ddcae73Sreyk 	snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
928ddcae73Sreyk 	    ifc->ifc_name, unit);
938ddcae73Sreyk 
94443a876bSangelos 	if_attach(ifp);
9511544374Sreyk 	if (unit == 0)
9611544374Sreyk 		if_addgroup(ifp, ifc->ifc_name);
97762caf3fSmpi 	/*
987ffb277fSbluhm 	 * enc(4) does not have a link-layer address but rtrequest()
9988be4c56Smpi 	 * wants an ifa for every route entry.  So let's setup a fake
10088be4c56Smpi 	 * and empty ifa of type AF_LINK for this purpose.
101762caf3fSmpi 	 */
102d6cd50e9Sitojun 	if_alloc_sadl(ifp);
103*18a44669Sbluhm 	refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
10488be4c56Smpi 	sc->sc_ifa.ifa_ifp = ifp;
105c7b7b779Sbluhm 	sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
10688be4c56Smpi 	sc->sc_ifa.ifa_netmask = NULL;
1072ef46f12Sprovos 
108dff15f04Sangelos #if NBPFILTER > 0
1098ddcae73Sreyk 	bpfattach(&ifp->if_bpf, ifp, DLT_ENC, ENC_HDRLEN);
110dff15f04Sangelos #endif
111aa28b9a6Smpi 	NET_LOCK();
112ad5900faSmpi 	error = enc_setif(ifp, 0);
113ad5900faSmpi 	if (error != 0) {
114aa28b9a6Smpi 		NET_UNLOCK();
1158ddcae73Sreyk 		if_detach(ifp);
116210de69cSmpi 		free(sc, M_DEVBUF, sizeof(*sc));
117ec93e248Sbluhm 		return (error);
118443a876bSangelos 	}
119443a876bSangelos 
120fcd035e8Sjca 	if (enc_allifps == NULL || unit > enc_max_unit) {
121f8238f3eSdoug 		if ((new = mallocarray(unit + 1, sizeof(struct ifnet *),
122ad5900faSmpi 		    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) {
123aa28b9a6Smpi 			NET_UNLOCK();
124f8238f3eSdoug 			return (ENOBUFS);
125ad5900faSmpi 		}
126a43d4d9bSreyk 
127a43d4d9bSreyk 		if (enc_allifps != NULL) {
1288a828045Sflorian 			oldlen = sizeof(struct ifnet *) * (enc_max_unit + 1);
1298a828045Sflorian 			memcpy(new, enc_allifps, oldlen);
1308a828045Sflorian 			free(enc_allifps, M_DEVBUF, oldlen);
131a43d4d9bSreyk 		}
132a43d4d9bSreyk 		enc_allifps = new;
133a43d4d9bSreyk 		enc_max_unit = unit;
134a43d4d9bSreyk 	}
135a43d4d9bSreyk 	enc_allifps[unit] = ifp;
136aa28b9a6Smpi 	NET_UNLOCK();
137a43d4d9bSreyk 
138603a9e02Sprovos 	return (0);
139da1356efSangelos }
140da1356efSangelos 
14145afdb40Sderaadt int
enc_clone_destroy(struct ifnet * ifp)1428ddcae73Sreyk enc_clone_destroy(struct ifnet *ifp)
14345afdb40Sderaadt {
1448ddcae73Sreyk 	struct enc_softc	*sc = ifp->if_softc;
1458ddcae73Sreyk 
1468ddcae73Sreyk 	/* Protect users from removing enc0 */
1478ddcae73Sreyk 	if (sc->sc_unit == 0)
1488ddcae73Sreyk 		return (EPERM);
1498ddcae73Sreyk 
150aa28b9a6Smpi 	NET_LOCK();
151a43d4d9bSreyk 	enc_allifps[sc->sc_unit] = NULL;
1528ddcae73Sreyk 	enc_unsetif(ifp);
153aa28b9a6Smpi 	NET_UNLOCK();
154ad5900faSmpi 
1558ddcae73Sreyk 	if_detach(ifp);
156*18a44669Sbluhm 	if (refcnt_rele(&sc->sc_ifa.ifa_refcnt) == 0) {
157*18a44669Sbluhm 		panic("%s: ifa refcnt has %u refs", __func__,
158*18a44669Sbluhm 		    sc->sc_ifa.ifa_refcnt.r_refs);
159*18a44669Sbluhm 	}
160210de69cSmpi 	free(sc, M_DEVBUF, sizeof(*sc));
1618ddcae73Sreyk 
1628ddcae73Sreyk 	return (0);
1638ddcae73Sreyk }
1648ddcae73Sreyk 
1658ddcae73Sreyk int
enc_output(struct ifnet * ifp,struct mbuf * m,struct sockaddr * sa,struct rtentry * rt)1668ddcae73Sreyk enc_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
1678ddcae73Sreyk     struct rtentry *rt)
1688ddcae73Sreyk {
1698ddcae73Sreyk 	m_freem(m);	/* drop packet */
1703aae6309Skrw 	return (EAFNOSUPPORT);
1718ddcae73Sreyk }
1728ddcae73Sreyk 
1738ddcae73Sreyk int
enc_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)1748ddcae73Sreyk enc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
1758ddcae73Sreyk {
1768ddcae73Sreyk 	struct ifreq	*ifr = (struct ifreq *)data;
177ec93e248Sbluhm 	int		 error;
1788ddcae73Sreyk 
17910f9ee35Sreyk 	switch (cmd) {
1808ddcae73Sreyk 	case SIOCSIFADDR:
181d6d49323Sangelos 	case SIOCSIFDSTADDR:
182da1356efSangelos 	case SIOCSIFFLAGS:
183fc1f920fSangelos 		if (ifp->if_flags & IFF_UP)
184fc1f920fSangelos 			ifp->if_flags |= IFF_RUNNING;
185fc1f920fSangelos 		else
186fc1f920fSangelos 			ifp->if_flags &= ~IFF_RUNNING;
187d6d49323Sangelos 		break;
1888bb39f08Sguenther 	case SIOCSIFRDOMAIN:
1898ddcae73Sreyk 		if ((error = enc_setif(ifp, ifr->ifr_rdomainid)) != 0)
1908ddcae73Sreyk 			return (error);
1918ddcae73Sreyk 		/* FALLTHROUGH */
19245afdb40Sderaadt 	default:
193e7581027Sbrad 		return (ENOTTY);
19445afdb40Sderaadt 	}
1959b14b002Sangelos 
1968ddcae73Sreyk 	return (0);
1978ddcae73Sreyk }
1988ddcae73Sreyk 
1998ddcae73Sreyk struct ifnet *
enc_getif(u_int rdomain,u_int unit)200e1bf84faSjca enc_getif(u_int rdomain, u_int unit)
2018ddcae73Sreyk {
202a43d4d9bSreyk 	struct ifnet	*ifp;
203a43d4d9bSreyk 
20478767d47Smpi 	NET_ASSERT_LOCKED();
20578767d47Smpi 
206a43d4d9bSreyk 	/* Check if the caller wants to get a non-default enc interface */
207a43d4d9bSreyk 	if (unit > 0) {
208a43d4d9bSreyk 		if (unit > enc_max_unit)
209a43d4d9bSreyk 			return (NULL);
210a43d4d9bSreyk 		ifp = enc_allifps[unit];
211e1bf84faSjca 		if (ifp == NULL || ifp->if_rdomain != rdomain)
212a43d4d9bSreyk 			return (NULL);
213a43d4d9bSreyk 		return (ifp);
214a43d4d9bSreyk 	}
215a43d4d9bSreyk 
216a43d4d9bSreyk 	/* Otherwise return the default enc interface for this rdomain */
2178ddcae73Sreyk 	if (enc_ifps == NULL)
2188ddcae73Sreyk 		return (NULL);
219e1bf84faSjca 	else if (rdomain > RT_TABLEID_MAX)
2208ddcae73Sreyk 		return (NULL);
221e1bf84faSjca 	else if (rdomain > enc_max_rdomain)
2228ddcae73Sreyk 		return (NULL);
223e1bf84faSjca 	return (enc_ifps[rdomain]);
2248ddcae73Sreyk }
2258ddcae73Sreyk 
226ad5900faSmpi struct ifaddr *
enc_getifa(u_int rdomain,u_int unit)227e1bf84faSjca enc_getifa(u_int rdomain, u_int unit)
22888be4c56Smpi {
22988be4c56Smpi 	struct ifnet		*ifp;
23088be4c56Smpi 	struct enc_softc	*sc;
23188be4c56Smpi 
232e1bf84faSjca 	ifp = enc_getif(rdomain, unit);
23388be4c56Smpi 	if (ifp == NULL)
23488be4c56Smpi 		return (NULL);
23588be4c56Smpi 
23688be4c56Smpi 	sc = ifp->if_softc;
23788be4c56Smpi 	return (&sc->sc_ifa);
23888be4c56Smpi }
2398ddcae73Sreyk int
enc_setif(struct ifnet * ifp,u_int rdomain)240e1bf84faSjca enc_setif(struct ifnet *ifp, u_int rdomain)
2418ddcae73Sreyk {
2428ddcae73Sreyk 	struct ifnet	**new;
2438a828045Sflorian 	size_t		 oldlen;
2448ddcae73Sreyk 
245ad5900faSmpi 	NET_ASSERT_LOCKED();
246ad5900faSmpi 
2478ddcae73Sreyk 	enc_unsetif(ifp);
2488ddcae73Sreyk 
2498ddcae73Sreyk 	/*
2508ddcae73Sreyk 	 * There can only be one default encif per rdomain -
2518ddcae73Sreyk 	 * Don't overwrite the existing enc iface that is stored
2528ddcae73Sreyk 	 * for this rdomain, so only the first enc interface that
2538ddcae73Sreyk 	 * was added for this rdomain becomes the default.
2548ddcae73Sreyk 	 */
255e1bf84faSjca 	if (enc_getif(rdomain, 0) != NULL)
2568ddcae73Sreyk 		return (0);
2578ddcae73Sreyk 
258e1bf84faSjca 	if (rdomain > RT_TABLEID_MAX)
259ec93e248Sbluhm 		return (EINVAL);
2608ddcae73Sreyk 
261e1bf84faSjca 	if (enc_ifps == NULL || rdomain > enc_max_rdomain) {
262e1bf84faSjca 		if ((new = mallocarray(rdomain + 1, sizeof(struct ifnet *),
263f8238f3eSdoug 		    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
264f8238f3eSdoug 			return (ENOBUFS);
2658ddcae73Sreyk 
2668ddcae73Sreyk 		if (enc_ifps != NULL) {
2678a828045Sflorian 			oldlen = sizeof(struct ifnet *) * (enc_max_rdomain + 1);
2688a828045Sflorian 			memcpy(new, enc_ifps, oldlen);
2698a828045Sflorian 			free(enc_ifps, M_DEVBUF, oldlen);
2708ddcae73Sreyk 		}
2718ddcae73Sreyk 		enc_ifps = new;
272e1bf84faSjca 		enc_max_rdomain = rdomain;
2738ddcae73Sreyk 	}
2748ddcae73Sreyk 
275e1bf84faSjca 	enc_ifps[rdomain] = ifp;
2768ddcae73Sreyk 
2778ddcae73Sreyk 	/* Indicate that this interface is the rdomain default */
2788ddcae73Sreyk 	ifp->if_link_state = LINK_STATE_UP;
2798ddcae73Sreyk 
2808ddcae73Sreyk 	return (0);
2818ddcae73Sreyk }
2828ddcae73Sreyk 
2838ddcae73Sreyk void
enc_unsetif(struct ifnet * ifp)2848ddcae73Sreyk enc_unsetif(struct ifnet *ifp)
2858ddcae73Sreyk {
286e1bf84faSjca 	u_int			 rdomain = ifp->if_rdomain, i;
287a43d4d9bSreyk 	struct ifnet		*oifp, *nifp;
2888ddcae73Sreyk 
289e1bf84faSjca 	if ((oifp = enc_getif(rdomain, 0)) == NULL || oifp != ifp)
2908ddcae73Sreyk 		return;
2918ddcae73Sreyk 
2928ddcae73Sreyk 	/* Clear slot for this rdomain */
293e1bf84faSjca 	enc_ifps[rdomain] = NULL;
2948ddcae73Sreyk 	ifp->if_link_state = LINK_STATE_UNKNOWN;
2958ddcae73Sreyk 
2968ddcae73Sreyk 	/*
2978ddcae73Sreyk 	 * Now find the next available encif to be the default interface
2988ddcae73Sreyk 	 * for this rdomain.
2998ddcae73Sreyk 	 */
300a43d4d9bSreyk 	for (i = 0; i < (enc_max_unit + 1); i++) {
301a43d4d9bSreyk 		nifp = enc_allifps[i];
302a43d4d9bSreyk 
303e1bf84faSjca 		if (nifp == NULL || nifp == ifp || nifp->if_rdomain != rdomain)
3048ddcae73Sreyk 			continue;
3058ddcae73Sreyk 
306e1bf84faSjca 		enc_ifps[rdomain] = nifp;
307a43d4d9bSreyk 		nifp->if_link_state = LINK_STATE_UP;
3088ddcae73Sreyk 		break;
3098ddcae73Sreyk 	}
31045afdb40Sderaadt }
311