1 /* $OpenBSD: mpls_input.c,v 1.73 2019/01/27 05:13:04 dlg Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Claudio Jeker <claudio@openbsd.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 19 #include "mpe.h" 20 21 #include <sys/param.h> 22 #include <sys/mbuf.h> 23 #include <sys/systm.h> 24 #include <sys/socket.h> 25 26 #include <net/if.h> 27 #include <net/if_var.h> 28 #include <net/if_types.h> 29 #include <net/netisr.h> 30 #include <net/route.h> 31 32 #include <netinet/in.h> 33 #include <netinet/ip.h> 34 #include <netinet/ip_var.h> 35 #include <netinet/ip_icmp.h> 36 37 #ifdef INET6 38 #include <netinet/ip6.h> 39 #endif /* INET6 */ 40 41 #include <netmpls/mpls.h> 42 43 #ifdef MPLS_DEBUG 44 #define MPLS_LABEL_GET(l) ((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET) 45 #define MPLS_TTL_GET(l) (ntohl((l) & MPLS_TTL_MASK)) 46 #endif 47 48 struct mbuf *mpls_do_error(struct mbuf *, int, int, int); 49 void mpls_input_local(struct rtentry *, struct mbuf *); 50 51 void 52 mpls_input(struct ifnet *ifp, struct mbuf *m) 53 { 54 struct sockaddr_mpls *smpls; 55 struct sockaddr_mpls sa_mpls; 56 struct shim_hdr *shim; 57 struct rtentry *rt; 58 struct rt_mpls *rt_mpls; 59 uint8_t ttl; 60 int hasbos; 61 62 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) { 63 m_freem(m); 64 return; 65 } 66 67 /* drop all broadcast and multicast packets */ 68 if (m->m_flags & (M_BCAST | M_MCAST)) { 69 m_freem(m); 70 return; 71 } 72 73 if (m->m_len < sizeof(*shim)) { 74 m = m_pullup(m, sizeof(*shim)); 75 if (m == NULL) 76 return; 77 } 78 79 shim = mtod(m, struct shim_hdr *); 80 #ifdef MPLS_DEBUG 81 printf("mpls_input: iface %s label=%d, ttl=%d BoS %d\n", 82 ifp->if_xname, MPLS_LABEL_GET(shim->shim_label), 83 MPLS_TTL_GET(shim->shim_label), 84 MPLS_BOS_ISSET(shim->shim_label)); 85 #endif 86 87 /* check and decrement TTL */ 88 ttl = ntohl(shim->shim_label & MPLS_TTL_MASK); 89 if (ttl <= 1) { 90 /* TTL exceeded */ 91 m = mpls_do_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0); 92 if (m == NULL) 93 return; 94 95 shim = mtod(m, struct shim_hdr *); 96 ttl = ntohl(shim->shim_label & MPLS_TTL_MASK); 97 } else 98 ttl--; 99 hasbos = MPLS_BOS_ISSET(shim->shim_label); 100 101 bzero(&sa_mpls, sizeof(sa_mpls)); 102 smpls = &sa_mpls; 103 smpls->smpls_family = AF_MPLS; 104 smpls->smpls_len = sizeof(*smpls); 105 smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK; 106 107 if (ntohl(smpls->smpls_label) < MPLS_LABEL_RESERVED_MAX) { 108 m = mpls_shim_pop(m); 109 if (m == NULL) 110 return; 111 if (!hasbos) { 112 /* 113 * RFC 4182 relaxes the position of the 114 * explicit NULL labels. They no longer need 115 * to be at the beginning of the stack. 116 * In this case the label is ignored and the decision 117 * is made based on the lower one. 118 */ 119 shim = mtod(m, struct shim_hdr *); 120 smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK; 121 hasbos = MPLS_BOS_ISSET(shim->shim_label); 122 } else { 123 switch (ntohl(smpls->smpls_label)) { 124 case MPLS_LABEL_IPV4NULL: 125 do_v4: 126 if (mpls_mapttl_ip) { 127 m = mpls_ip_adjttl(m, ttl); 128 if (m == NULL) 129 return; 130 } 131 ipv4_input(ifp, m); 132 return; 133 #ifdef INET6 134 case MPLS_LABEL_IPV6NULL: 135 do_v6: 136 if (mpls_mapttl_ip6) { 137 m = mpls_ip6_adjttl(m, ttl); 138 if (m == NULL) 139 return; 140 } 141 ipv6_input(ifp, m); 142 return; 143 #endif /* INET6 */ 144 case MPLS_LABEL_IMPLNULL: 145 if (m->m_len < sizeof(u_char) && 146 (m = m_pullup(m, sizeof(u_char))) == NULL) 147 return; 148 switch (*mtod(m, u_char *) >> 4) { 149 case IPVERSION: 150 goto do_v4; 151 #ifdef INET6 152 case IPV6_VERSION >> 4: 153 goto do_v6; 154 #endif 155 default: 156 m_freem(m); 157 return; 158 } 159 default: 160 /* Other cases are not handled for now */ 161 m_freem(m); 162 return; 163 } 164 } 165 } 166 167 ifp = NULL; 168 169 rt = rtalloc(smplstosa(smpls), RT_RESOLVE, m->m_pkthdr.ph_rtableid); 170 if (rt == NULL) { 171 /* no entry for this label */ 172 #ifdef MPLS_DEBUG 173 printf("MPLS_DEBUG: label not found\n"); 174 #endif 175 m_freem(m); 176 return; 177 } 178 179 rt_mpls = (struct rt_mpls *)rt->rt_llinfo; 180 if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) { 181 #ifdef MPLS_DEBUG 182 printf("MPLS_DEBUG: no MPLS information attached\n"); 183 #endif 184 m_freem(m); 185 goto done; 186 } 187 188 switch (rt_mpls->mpls_operation) { 189 case MPLS_OP_POP: 190 if (ISSET(rt->rt_flags, RTF_LOCAL)) { 191 mpls_input_local(rt, m); 192 goto done; 193 } 194 195 m = mpls_shim_pop(m); 196 if (m == NULL) 197 goto done; 198 if (!hasbos) 199 /* just forward to gw */ 200 break; 201 202 /* last label popped so decide where to push it to */ 203 ifp = if_get(rt->rt_ifidx); 204 if (ifp == NULL) { 205 m_freem(m); 206 goto done; 207 } 208 209 KASSERT(rt->rt_gateway); 210 211 switch(rt->rt_gateway->sa_family) { 212 case AF_INET: 213 if ((m = mpls_ip_adjttl(m, ttl)) == NULL) 214 goto done; 215 break; 216 #ifdef INET6 217 case AF_INET6: 218 if ((m = mpls_ip6_adjttl(m, ttl)) == NULL) 219 goto done; 220 break; 221 #endif 222 case AF_LINK: 223 break; 224 default: 225 m_freem(m); 226 goto done; 227 } 228 229 /* shortcut sending out the packet */ 230 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) 231 (*ifp->if_output)(ifp, m, rt->rt_gateway, rt); 232 else 233 (*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt); 234 goto done; 235 case MPLS_OP_PUSH: 236 /* this does not make much sense but it does not hurt */ 237 m = mpls_shim_push(m, rt_mpls); 238 break; 239 case MPLS_OP_SWAP: 240 m = mpls_shim_swap(m, rt_mpls); 241 break; 242 default: 243 m_freem(m); 244 goto done; 245 } 246 247 if (m == NULL) 248 goto done; 249 250 /* refetch label and write back TTL */ 251 shim = mtod(m, struct shim_hdr *); 252 shim->shim_label = (shim->shim_label & ~MPLS_TTL_MASK) | htonl(ttl); 253 254 ifp = if_get(rt->rt_ifidx); 255 if (ifp == NULL) { 256 m_freem(m); 257 goto done; 258 } 259 #ifdef MPLS_DEBUG 260 printf("MPLS: sending on %s outlabel %x dst af %d in %d out %d\n", 261 ifp->if_xname, ntohl(shim->shim_label), smpls->smpls_family, 262 MPLS_LABEL_GET(smpls->smpls_label), 263 MPLS_LABEL_GET(rt_mpls->mpls_label)); 264 #endif 265 266 /* Output iface is not MPLS-enabled */ 267 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) { 268 #ifdef MPLS_DEBUG 269 printf("MPLS_DEBUG: interface %s not mpls enabled\n", 270 ifp->if_xname); 271 #endif 272 m_freem(m); 273 goto done; 274 } 275 276 (*ifp->if_ll_output)(ifp, m, smplstosa(smpls), rt); 277 done: 278 if_put(ifp); 279 rtfree(rt); 280 } 281 282 void 283 mpls_input_local(struct rtentry *rt, struct mbuf *m) 284 { 285 struct ifnet *ifp; 286 287 ifp = if_get(rt->rt_ifidx); 288 if (ifp == NULL) { 289 m_freem(m); 290 return; 291 } 292 293 /* shortcut sending out the packet */ 294 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) 295 (*ifp->if_output)(ifp, m, rt->rt_gateway, rt); 296 else 297 (*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt); 298 299 if_put(ifp); 300 } 301 302 struct mbuf * 303 mpls_ip_adjttl(struct mbuf *m, u_int8_t ttl) 304 { 305 struct ip *ip; 306 uint16_t old, new, x; 307 308 if (m->m_len < sizeof(*ip)) { 309 m = m_pullup(m, sizeof(*ip)); 310 if (m == NULL) 311 return (NULL); 312 } 313 ip = mtod(m, struct ip *); 314 315 old = htons(ip->ip_ttl << 8); 316 new = htons(ttl << 8); 317 x = ip->ip_sum + old - new; 318 319 ip->ip_ttl = ttl; 320 ip->ip_sum = (x) + (x >> 16); 321 322 return (m); 323 } 324 325 #ifdef INET6 326 struct mbuf * 327 mpls_ip6_adjttl(struct mbuf *m, u_int8_t ttl) 328 { 329 struct ip6_hdr *ip6; 330 331 if (m->m_len < sizeof(*ip6)) { 332 m = m_pullup(m, sizeof(*ip6)); 333 if (m == NULL) 334 return (NULL); 335 } 336 ip6 = mtod(m, struct ip6_hdr *); 337 338 ip6->ip6_hlim = ttl; 339 340 return (m); 341 } 342 #endif /* INET6 */ 343 344 struct mbuf * 345 mpls_do_error(struct mbuf *m, int type, int code, int destmtu) 346 { 347 struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX]; 348 struct sockaddr_mpls sa_mpls; 349 struct sockaddr_mpls *smpls; 350 struct rtentry *rt = NULL; 351 struct shim_hdr *shim; 352 struct in_ifaddr *ia; 353 struct icmp *icp; 354 struct ip *ip; 355 int nstk, error; 356 357 for (nstk = 0; nstk < MPLS_INKERNEL_LOOP_MAX; nstk++) { 358 if (m->m_len < sizeof(*shim) && 359 (m = m_pullup(m, sizeof(*shim))) == NULL) 360 return (NULL); 361 stack[nstk] = *mtod(m, struct shim_hdr *); 362 m_adj(m, sizeof(*shim)); 363 if (MPLS_BOS_ISSET(stack[nstk].shim_label)) 364 break; 365 } 366 shim = &stack[0]; 367 368 if (m->m_len < sizeof(u_char) && 369 (m = m_pullup(m, sizeof(u_char))) == NULL) 370 return (NULL); 371 switch (*mtod(m, u_char *) >> 4) { 372 case IPVERSION: 373 if (m->m_len < sizeof(*ip) && 374 (m = m_pullup(m, sizeof(*ip))) == NULL) 375 return (NULL); 376 m = icmp_do_error(m, type, code, 0, destmtu); 377 if (m == NULL) 378 return (NULL); 379 380 if (icmp_do_exthdr(m, ICMP_EXT_MPLS, 1, stack, 381 (nstk + 1) * sizeof(*shim))) 382 return (NULL); 383 384 /* set ip_src to something usable, based on the MPLS label */ 385 bzero(&sa_mpls, sizeof(sa_mpls)); 386 smpls = &sa_mpls; 387 smpls->smpls_family = AF_MPLS; 388 smpls->smpls_len = sizeof(*smpls); 389 smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK; 390 391 rt = rtalloc(smplstosa(smpls), RT_RESOLVE, 0); 392 if (rt == NULL) { 393 /* no entry for this label */ 394 m_freem(m); 395 return (NULL); 396 } 397 if (rt->rt_ifa->ifa_addr->sa_family == AF_INET) 398 ia = ifatoia(rt->rt_ifa); 399 else { 400 /* XXX this needs fixing, if the MPLS is on an IP 401 * less interface we need to find some other IP to 402 * use as source. 403 */ 404 rtfree(rt); 405 m_freem(m); 406 return (NULL); 407 } 408 /* It is safe to dereference ``ia'' iff ``rt'' is valid. */ 409 error = icmp_reflect(m, NULL, ia); 410 rtfree(rt); 411 if (error) 412 return (NULL); 413 414 ip = mtod(m, struct ip *); 415 /* stuff to fix up which is normaly done in ip_output */ 416 ip->ip_v = IPVERSION; 417 ip->ip_id = htons(ip_randomid()); 418 ip->ip_sum = 0; 419 ip->ip_sum = in_cksum(m, sizeof(*ip)); 420 421 /* stolen from icmp_send() */ 422 icp = (struct icmp *)(mtod(m, caddr_t) + sizeof(*ip)); 423 icp->icmp_cksum = 0; 424 icp->icmp_cksum = in4_cksum(m, 0, sizeof(*ip), 425 ntohs(ip->ip_len) - sizeof(*ip)); 426 427 break; 428 #ifdef INET6 429 case IPV6_VERSION >> 4: 430 #endif 431 default: 432 m_freem(m); 433 return (NULL); 434 } 435 436 /* add mpls stack back to new packet */ 437 M_PREPEND(m, (nstk + 1) * sizeof(*shim), M_NOWAIT); 438 if (m == NULL) 439 return (NULL); 440 m_copyback(m, 0, (nstk + 1) * sizeof(*shim), stack, M_NOWAIT); 441 442 /* change TTL to default */ 443 shim = mtod(m, struct shim_hdr *); 444 shim->shim_label = 445 (shim->shim_label & ~MPLS_TTL_MASK) | htonl(mpls_defttl); 446 447 return (m); 448 } 449