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