1 /* $OpenBSD: if_mpe.c,v 1.9 2008/05/08 09:52:36 pyr Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@spootnik.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include "mpe.h" 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/mbuf.h> 23 #include <sys/proc.h> 24 #include <sys/socket.h> 25 #include <sys/sockio.h> 26 #include <sys/ioctl.h> 27 28 #include <net/if.h> 29 #include <net/if_types.h> 30 #include <net/netisr.h> 31 #include <net/route.h> 32 33 #ifdef INET 34 #include <netinet/in.h> 35 #include <netinet/in_var.h> 36 #include <netinet/in_systm.h> 37 #include <netinet/ip.h> 38 #endif 39 40 #ifdef INET6 41 #ifndef INET 42 #include <netinet/in.h> 43 #endif 44 #include <netinet6/nd6.h> 45 #endif /* INET6 */ 46 47 #include "bpfilter.h" 48 #if NBPFILTER > 0 49 #include <net/bpf.h> 50 #endif 51 52 #include <netmpls/mpls.h> 53 54 #ifdef MPLS_DEBUG 55 #define DPRINTF(x) do { if (mpedebug) printf x ; } while (0) 56 #else 57 #define DPRINTF(x) 58 #endif 59 60 void mpeattach(int); 61 int mpeoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 62 struct rtentry *); 63 int mpeioctl(struct ifnet *, u_long, caddr_t); 64 void mpestart(struct ifnet *); 65 int mpe_clone_create(struct if_clone *, int); 66 int mpe_clone_destroy(struct ifnet *); 67 68 LIST_HEAD(, mpe_softc) mpeif_list; 69 struct if_clone mpe_cloner = 70 IF_CLONE_INITIALIZER("mpe", mpe_clone_create, mpe_clone_destroy); 71 72 void 73 mpeattach(int nmpe) 74 { 75 LIST_INIT(&mpeif_list); 76 if_clone_attach(&mpe_cloner); 77 } 78 79 int 80 mpe_clone_create(struct if_clone *ifc, int unit) 81 { 82 struct ifnet *ifp; 83 struct mpe_softc *mpeif; 84 int s; 85 86 if ((mpeif = malloc(sizeof(*mpeif), 87 M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 88 return (ENOMEM); 89 90 mpeif->sc_shim.shim_label = MPLS_BOS_MASK | htonl(mpls_defttl); 91 mpeif->sc_unit = unit; 92 ifp = &mpeif->sc_if; 93 snprintf(ifp->if_xname, sizeof ifp->if_xname, "mpe%d", unit); 94 ifp->if_flags = IFF_POINTOPOINT; 95 ifp->if_softc = mpeif; 96 ifp->if_mtu = MPE_MTU; 97 ifp->if_ioctl = mpeioctl; 98 ifp->if_output = mpeoutput; 99 ifp->if_start = mpestart; 100 ifp->if_type = IFT_MPLS; 101 ifp->if_snd.ifq_maxlen = ifqmaxlen; 102 ifp->if_hdrlen = MPE_HDRLEN; 103 IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 104 IFQ_SET_READY(&ifp->if_snd); 105 if_attach(ifp); 106 if_alloc_sadl(ifp); 107 #if NBPFILTER > 0 108 bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, MPE_HDRLEN); 109 #endif 110 111 s = splnet(); 112 LIST_INSERT_HEAD(&mpeif_list, mpeif, sc_list); 113 splx(s); 114 115 return (0); 116 } 117 118 int 119 mpe_clone_destroy(struct ifnet *ifp) 120 { 121 struct mpe_softc *mpeif = ifp->if_softc; 122 int s; 123 124 s = splnet(); 125 LIST_REMOVE(mpeif, sc_list); 126 splx(s); 127 128 #if NBPFILTER > 0 129 bpfdetach(ifp); 130 #endif 131 if_detach(ifp); 132 free(mpeif, M_DEVBUF); 133 return (0); 134 } 135 136 /* 137 * Start output on the mpe interface. 138 */ 139 void 140 mpestart(struct ifnet *ifp) 141 { 142 struct mbuf *m; 143 struct mpe_softc *ifm; 144 struct shim_hdr shim; 145 int s; 146 147 for (;;) { 148 s = splnet(); 149 IFQ_DEQUEUE(&ifp->if_snd, m); 150 splx(s); 151 152 if (m == NULL) 153 return; 154 155 #if NBPFILTER > 0 156 if (ifp->if_bpf) 157 bpf_mtap_af(ifp->if_bpf, AF_INET, m, BPF_DIRECTION_OUT); 158 #endif 159 ifm = ifp->if_softc; 160 shim.shim_label = ifm->sc_shim.shim_label; 161 M_PREPEND(m, sizeof(shim), M_DONTWAIT); 162 m_copyback(m, 0, sizeof(shim), (caddr_t)&shim); 163 if (m == NULL) { 164 ifp->if_ierrors++; 165 continue; 166 } 167 m->m_pkthdr.rcvif = ifp; 168 mpls_input(m); 169 } 170 } 171 172 int 173 mpeoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 174 struct rtentry *rt) 175 { 176 int s; 177 int error; 178 179 error = 0; 180 switch (dst->sa_family) { 181 case AF_INET: 182 break; 183 case AF_MPLS: 184 /* 185 * drop MPLS packets entering here. This is a hack to prevent 186 * loops because of misconfiguration. 187 */ 188 m_freem(m); 189 error = ENETUNREACH; 190 return (error); 191 default: 192 error = ENETDOWN; 193 goto out; 194 } 195 s = splnet(); 196 IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); 197 if (error) { 198 /* mbuf is already freed */ 199 splx(s); 200 return (error); 201 } 202 if_start(ifp); 203 splx(s); 204 out: 205 if (error) 206 ifp->if_oerrors++; 207 return (error); 208 } 209 210 /* ARGSUSED */ 211 int 212 mpeioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 213 { 214 int error; 215 struct mpe_softc *ifm; 216 struct ifreq *ifr; 217 struct shim_hdr shim; 218 u_int32_t ttl = htonl(mpls_defttl); 219 220 ifr = (struct ifreq *)data; 221 error = 0; 222 switch (cmd) { 223 case SIOCSIFADDR: 224 ifp->if_flags |= IFF_UP; 225 break; 226 case SIOCSIFFLAGS: 227 if (ifp->if_flags & IFF_UP) 228 ifp->if_flags |= IFF_RUNNING; 229 else 230 ifp->if_flags &= ~IFF_RUNNING; 231 break; 232 case SIOCSIFMTU: 233 if (ifr->ifr_mtu < MPE_MTU_MIN || 234 ifr->ifr_mtu > MPE_MTU_MAX) 235 error = EINVAL; 236 else 237 ifp->if_mtu = ifr->ifr_mtu; 238 break; 239 case SIOCGETLABEL: 240 ifm = ifp->if_softc; 241 shim.shim_label = 242 ((ntohl(ifm->sc_shim.shim_label & MPLS_LABEL_MASK)) >> 243 MPLS_LABEL_OFFSET); 244 error = copyout(&shim, ifr->ifr_data, sizeof(shim)); 245 break; 246 case SIOCSETLABEL: 247 ifm = ifp->if_softc; 248 if ((error = copyin(ifr->ifr_data, &shim, sizeof(shim)))) 249 break; 250 if (shim.shim_label > MPLS_LABEL_MAX) { 251 error = EINVAL; 252 break; 253 } 254 shim.shim_label = (htonl(shim.shim_label << MPLS_LABEL_OFFSET)) 255 | MPLS_BOS_MASK | ttl; 256 if (ifm->sc_shim.shim_label == shim.shim_label) 257 break; 258 LIST_FOREACH(ifm, &mpeif_list, sc_list) { 259 if (ifm != ifp->if_softc && 260 ifm->sc_shim.shim_label == shim.shim_label) { 261 error = EEXIST; 262 break; 263 } 264 } 265 if (error) 266 break; 267 ifm = ifp->if_softc; 268 ifm->sc_shim.shim_label = shim.shim_label; 269 break; 270 default: 271 return (ENOTTY); 272 } 273 274 return (error); 275 } 276 277 void 278 mpe_input(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls, 279 u_int32_t ttl) 280 { 281 int s; 282 283 /* fixup ttl */ 284 /* label -> AF lookup */ 285 286 #if NBPFILTER > 0 287 if (ifp->if_bpf) 288 bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); 289 #endif 290 s = splnet(); 291 /* 292 * assume we only get fed ipv4 packets for now. 293 */ 294 IF_ENQUEUE(&ipintrq, m); 295 schednetisr(NETISR_IP); 296 splx(s); 297 } 298