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