1 /* $OpenBSD: if_mpe.c,v 1.16 2009/07/13 12:41:46 dlg 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 #include <netinet/ip6.h> 42 #ifndef INET 43 #include <netinet/in.h> 44 #endif 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 extern int mpls_mapttl_ip; 73 extern int mpls_mapttl_ip6; 74 75 void 76 mpeattach(int nmpe) 77 { 78 LIST_INIT(&mpeif_list); 79 if_clone_attach(&mpe_cloner); 80 } 81 82 int 83 mpe_clone_create(struct if_clone *ifc, int unit) 84 { 85 struct ifnet *ifp; 86 struct mpe_softc *mpeif; 87 int s; 88 89 if ((mpeif = malloc(sizeof(*mpeif), 90 M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 91 return (ENOMEM); 92 93 mpeif->sc_shim.shim_label = MPLS_BOS_MASK | htonl(mpls_defttl); 94 mpeif->sc_unit = unit; 95 ifp = &mpeif->sc_if; 96 snprintf(ifp->if_xname, sizeof ifp->if_xname, "mpe%d", unit); 97 ifp->if_flags = IFF_POINTOPOINT; 98 ifp->if_softc = mpeif; 99 ifp->if_mtu = MPE_MTU; 100 ifp->if_ioctl = mpeioctl; 101 ifp->if_output = mpeoutput; 102 ifp->if_start = mpestart; 103 ifp->if_type = IFT_MPLS; 104 ifp->if_hdrlen = MPE_HDRLEN; 105 IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 106 IFQ_SET_READY(&ifp->if_snd); 107 if_attach(ifp); 108 if_alloc_sadl(ifp); 109 #if NBPFILTER > 0 110 bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, MPE_HDRLEN); 111 #endif 112 113 s = splnet(); 114 LIST_INSERT_HEAD(&mpeif_list, mpeif, sc_list); 115 splx(s); 116 117 return (0); 118 } 119 120 int 121 mpe_clone_destroy(struct ifnet *ifp) 122 { 123 struct mpe_softc *mpeif = ifp->if_softc; 124 int s; 125 126 s = splnet(); 127 LIST_REMOVE(mpeif, sc_list); 128 splx(s); 129 130 #if NBPFILTER > 0 131 bpfdetach(ifp); 132 #endif 133 if_detach(ifp); 134 free(mpeif, M_DEVBUF); 135 return (0); 136 } 137 138 /* 139 * Start output on the mpe interface. 140 */ 141 void 142 mpestart(struct ifnet *ifp) 143 { 144 struct mbuf *m; 145 struct mpe_softc *ifm; 146 struct shim_hdr shim; 147 int s; 148 149 for (;;) { 150 s = splnet(); 151 IFQ_DEQUEUE(&ifp->if_snd, m); 152 splx(s); 153 154 if (m == NULL) 155 return; 156 157 #if NBPFILTER > 0 158 if (ifp->if_bpf) 159 bpf_mtap_af(ifp->if_bpf, AF_INET, m, BPF_DIRECTION_OUT); 160 #endif 161 ifm = ifp->if_softc; 162 shim.shim_label = ifm->sc_shim.shim_label; 163 M_PREPEND(m, sizeof(shim), M_DONTWAIT); 164 m_copyback(m, 0, sizeof(shim), (caddr_t)&shim); 165 if (m == NULL) { 166 ifp->if_ierrors++; 167 continue; 168 } 169 m->m_pkthdr.rcvif = ifp; 170 mpls_output(m, NULL); 171 } 172 } 173 174 int 175 mpeoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 176 struct rtentry *rt) 177 { 178 int s; 179 int error; 180 181 error = 0; 182 switch (dst->sa_family) { 183 case AF_INET: 184 break; 185 case AF_MPLS: 186 /* 187 * drop MPLS packets entering here. This is a hack to prevent 188 * loops because of misconfiguration. 189 */ 190 m_freem(m); 191 error = ENETUNREACH; 192 return (error); 193 default: 194 error = ENETDOWN; 195 goto out; 196 } 197 s = splnet(); 198 IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); 199 if (error) { 200 /* mbuf is already freed */ 201 splx(s); 202 return (error); 203 } 204 if_start(ifp); 205 splx(s); 206 out: 207 if (error) 208 ifp->if_oerrors++; 209 return (error); 210 } 211 212 /* ARGSUSED */ 213 int 214 mpeioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 215 { 216 int error; 217 struct mpe_softc *ifm; 218 struct ifreq *ifr; 219 struct shim_hdr shim; 220 u_int32_t ttl = htonl(mpls_defttl); 221 222 ifr = (struct ifreq *)data; 223 error = 0; 224 switch (cmd) { 225 case SIOCSIFADDR: 226 ifp->if_flags |= IFF_UP; 227 break; 228 case SIOCSIFFLAGS: 229 if (ifp->if_flags & IFF_UP) 230 ifp->if_flags |= IFF_RUNNING; 231 else 232 ifp->if_flags &= ~IFF_RUNNING; 233 break; 234 case SIOCSIFMTU: 235 if (ifr->ifr_mtu < MPE_MTU_MIN || 236 ifr->ifr_mtu > MPE_MTU_MAX) 237 error = EINVAL; 238 else 239 ifp->if_mtu = ifr->ifr_mtu; 240 break; 241 case SIOCGETLABEL: 242 ifm = ifp->if_softc; 243 shim.shim_label = 244 ((ntohl(ifm->sc_shim.shim_label & MPLS_LABEL_MASK)) >> 245 MPLS_LABEL_OFFSET); 246 error = copyout(&shim, ifr->ifr_data, sizeof(shim)); 247 break; 248 case SIOCSETLABEL: 249 ifm = ifp->if_softc; 250 if ((error = copyin(ifr->ifr_data, &shim, sizeof(shim)))) 251 break; 252 if (shim.shim_label > MPLS_LABEL_MAX || 253 shim.shim_label <= MPLS_LABEL_RESERVED_MAX) { 254 error = EINVAL; 255 break; 256 } 257 shim.shim_label = (htonl(shim.shim_label << MPLS_LABEL_OFFSET)) 258 | MPLS_BOS_MASK | ttl; 259 if (ifm->sc_shim.shim_label == shim.shim_label) 260 break; 261 LIST_FOREACH(ifm, &mpeif_list, sc_list) { 262 if (ifm != ifp->if_softc && 263 ifm->sc_shim.shim_label == shim.shim_label) { 264 error = EEXIST; 265 break; 266 } 267 } 268 if (error) 269 break; 270 ifm = ifp->if_softc; 271 ifm->sc_shim.shim_label = shim.shim_label; 272 break; 273 default: 274 return (ENOTTY); 275 } 276 277 return (error); 278 } 279 280 void 281 mpe_input(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls, 282 u_int8_t ttl) 283 { 284 struct ip *ip; 285 int s, hlen; 286 287 /* label -> AF lookup */ 288 289 if (mpls_mapttl_ip) { 290 if (m->m_len < sizeof (struct ip) && 291 (m = m_pullup(m, sizeof(struct ip))) == NULL) 292 return; 293 ip = mtod(m, struct ip *); 294 hlen = ip->ip_hl << 2; 295 if (m->m_len < hlen) { 296 if ((m = m_pullup(m, hlen)) == NULL) 297 return; 298 ip = mtod(m, struct ip *); 299 } 300 301 if (in_cksum(m, hlen) != 0) { 302 m_free(m); 303 return; 304 } 305 306 /* set IP ttl from MPLS ttl */ 307 ip->ip_ttl = ttl; 308 309 /* recalculate checksum */ 310 ip->ip_sum = 0; 311 ip->ip_sum = in_cksum(m, hlen); 312 } 313 314 #if NBPFILTER > 0 315 if (ifp && ifp->if_bpf) 316 bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); 317 #endif 318 s = splnet(); 319 IF_ENQUEUE(&ipintrq, m); 320 schednetisr(NETISR_IP); 321 splx(s); 322 } 323 324 void 325 mpe_input6(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls, 326 u_int8_t ttl) 327 { 328 struct ip6_hdr *ip6hdr; 329 int s; 330 331 /* label -> AF lookup */ 332 333 if (mpls_mapttl_ip6) { 334 if (m->m_len < sizeof (struct ip6_hdr) && 335 (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) 336 return; 337 338 ip6hdr = mtod(m, struct ip6_hdr *); 339 340 /* set IPv6 ttl from MPLS ttl */ 341 ip6hdr->ip6_hlim = ttl; 342 } 343 344 #if NBPFILTER > 0 345 if (ifp && ifp->if_bpf) 346 bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); 347 #endif 348 s = splnet(); 349 IF_ENQUEUE(&ip6intrq, m); 350 schednetisr(NETISR_IPV6); 351 splx(s); 352 } 353