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