1 /* $OpenBSD: if_pair.c,v 1.13 2019/04/27 05:16:15 dlg 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 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_start = pairstart; 120 ifp->if_xflags = IFXF_CLONED; 121 IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 122 123 ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN; 124 ifp->if_capabilities = IFCAP_VLAN_MTU; 125 126 ifmedia_init(&sc->sc_media, 0, pair_media_change, 127 pair_media_status); 128 ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); 129 ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); 130 131 if_attach(ifp); 132 ether_ifattach(ifp); 133 134 pair_link_state(ifp); 135 136 return (0); 137 } 138 139 int 140 pair_clone_destroy(struct ifnet *ifp) 141 { 142 struct pair_softc *sc = ifp->if_softc; 143 struct ifnet *pairedifp; 144 struct pair_softc *dstsc = ifp->if_softc; 145 146 if ((pairedifp = if_get(sc->sc_pairedif)) != NULL) { 147 dstsc = pairedifp->if_softc; 148 dstsc->sc_pairedif = 0; 149 pair_link_state(pairedifp); 150 if_put(pairedifp); 151 } 152 153 ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY); 154 ether_ifdetach(ifp); 155 if_detach(ifp); 156 free(sc, M_DEVBUF, sizeof(*sc)); 157 158 return (0); 159 } 160 161 void 162 pairstart(struct ifnet *ifp) 163 { 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 for (;;) { 172 IFQ_DEQUEUE(&ifp->if_snd, m); 173 if (m == NULL) 174 break; 175 176 #if NBPFILTER > 0 177 if (ifp->if_bpf) 178 bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); 179 #endif /* NBPFILTER > 0 */ 180 181 if (pairedifp != NULL) { 182 if (m->m_flags & M_PKTHDR) 183 m_resethdr(m); 184 ml_enqueue(&ml, m); 185 } else 186 m_freem(m); 187 } 188 189 if (pairedifp != NULL) { 190 if_input(pairedifp, &ml); 191 if_put(pairedifp); 192 } 193 } 194 195 int 196 pairioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 197 { 198 struct pair_softc *sc = (struct pair_softc *)ifp->if_softc; 199 struct ifreq *ifr = (struct ifreq *)data; 200 struct if_clone *ifc; 201 struct pair_softc *pairedsc = ifp->if_softc; 202 struct ifnet *oldifp = NULL, *newifp = NULL; 203 int error = 0, unit; 204 205 switch (cmd) { 206 case SIOCSIFADDR: 207 ifp->if_flags |= IFF_UP; 208 /* FALLTHROUGH */ 209 210 case SIOCSIFFLAGS: 211 if (ifp->if_flags & IFF_UP) 212 ifp->if_flags |= IFF_RUNNING; 213 else 214 ifp->if_flags &= ~IFF_RUNNING; 215 break; 216 217 case SIOCADDMULTI: 218 case SIOCDELMULTI: 219 break; 220 221 case SIOCGIFMEDIA: 222 case SIOCSIFMEDIA: 223 error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 224 break; 225 226 case SIOCSIFPAIR: 227 if (sc->sc_pairedif == ifr->ifr_index) 228 break; 229 230 /* Cannot link to myself */ 231 if (ifr->ifr_index == ifp->if_index) { 232 error = EINVAL; 233 break; 234 } 235 236 oldifp = if_get(sc->sc_pairedif); 237 newifp = if_get(ifr->ifr_index); 238 239 if (newifp != NULL) { 240 pairedsc = newifp->if_softc; 241 242 if (pairedsc->sc_pairedif != 0) { 243 error = EBUSY; 244 break; 245 } 246 247 /* Only allow pair(4) interfaces for the pair */ 248 if ((ifc = if_clone_lookup(newifp->if_xname, 249 &unit)) == NULL || strcmp("pair", 250 ifc->ifc_name) != 0) { 251 error = ENODEV; 252 break; 253 } 254 255 pairedsc->sc_pairedif = ifp->if_index; 256 sc->sc_pairedif = ifr->ifr_index; 257 } else 258 sc->sc_pairedif = 0; 259 260 if (oldifp != NULL) { 261 pairedsc = oldifp->if_softc; 262 pairedsc->sc_pairedif = 0; 263 } 264 break; 265 266 case SIOCGIFPAIR: 267 ifr->ifr_index = sc->sc_pairedif; 268 break; 269 270 default: 271 error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); 272 } 273 274 if (newifp != NULL || oldifp != NULL) 275 pair_link_state(ifp); 276 if (oldifp != NULL) { 277 pair_link_state(oldifp); 278 if_put(oldifp); 279 } 280 if (newifp != NULL) { 281 pair_link_state(newifp); 282 if_put(newifp); 283 } 284 285 return (error); 286 } 287