1/* $OpenBSD: mpls_output.c,v 1.29 2023/05/13 13:35:18 bluhm Exp $ */ 2 3/* 4 * Copyright (c) 2008 Claudio Jeker <claudio@openbsd.org> 5 * Copyright (c) 2008 Michele Marchetto <michele@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/mbuf.h> 22#include <sys/systm.h> 23#include <sys/socket.h> 24 25#include <net/if.h> 26#include <net/if_var.h> 27#include <net/route.h> 28 29#include <netmpls/mpls.h> 30 31#include <netinet/in.h> 32#include <netinet/ip.h> 33 34#ifdef INET6 35#include <netinet/ip6.h> 36#endif 37 38#ifdef MPLS_DEBUG 39#define MPLS_LABEL_GET(l) ((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET) 40#endif 41 42u_int8_t mpls_getttl(struct mbuf *, sa_family_t); 43 44int 45mpls_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 46 struct rtentry *rt) 47{ 48 struct sockaddr_mpls *smpls; 49 struct sockaddr_mpls sa_mpls; 50 struct shim_hdr *shim; 51 struct rt_mpls *rt_mpls; 52 int error; 53 u_int8_t ttl; 54 55 if (rt == NULL || (dst->sa_family != AF_INET && 56 dst->sa_family != AF_INET6 && dst->sa_family != AF_MPLS)) { 57 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) 58 return (ifp->if_output(ifp, m, dst, rt)); 59 else 60 return (ifp->if_ll_output(ifp, m, dst, rt)); 61 } 62 63 /* need to calculate checksums now if necessary */ 64 if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) 65 in_hdr_cksum_out(m, NULL); 66 in_proto_cksum_out(m, NULL); 67 68 /* initialize sockaddr_mpls */ 69 bzero(&sa_mpls, sizeof(sa_mpls)); 70 smpls = &sa_mpls; 71 smpls->smpls_family = AF_MPLS; 72 smpls->smpls_len = sizeof(*smpls); 73 74 ttl = mpls_getttl(m, dst->sa_family); 75 76 rt_mpls = (struct rt_mpls *)rt->rt_llinfo; 77 if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) { 78 /* no MPLS information for this entry */ 79 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) { 80#ifdef MPLS_DEBUG 81 printf("MPLS_DEBUG: interface not mpls enabled\n"); 82#endif 83 error = ENETUNREACH; 84 goto bad; 85 } 86 87 return (ifp->if_ll_output(ifp, m, dst, rt)); 88 } 89 90 /* to be honest here only the push operation makes sense */ 91 switch (rt_mpls->mpls_operation) { 92 case MPLS_OP_PUSH: 93 m = mpls_shim_push(m, rt_mpls); 94 break; 95 case MPLS_OP_POP: 96 m = mpls_shim_pop(m); 97 break; 98 case MPLS_OP_SWAP: 99 m = mpls_shim_swap(m, rt_mpls); 100 break; 101 default: 102 error = EINVAL; 103 goto bad; 104 } 105 106 if (m == NULL) { 107 error = ENOBUFS; 108 goto bad; 109 } 110 111 /* refetch label */ 112 shim = mtod(m, struct shim_hdr *); 113 /* mark first label with BOS flag */ 114 if (dst->sa_family != AF_MPLS) 115 shim->shim_label |= MPLS_BOS_MASK; 116 117 /* write back TTL */ 118 shim->shim_label &= ~MPLS_TTL_MASK; 119 shim->shim_label |= htonl(ttl); 120 121#ifdef MPLS_DEBUG 122 printf("MPLS: sending on %s outshim %x outlabel %d\n", 123 ifp->if_xname, ntohl(shim->shim_label), 124 MPLS_LABEL_GET(rt_mpls->mpls_label)); 125#endif 126 127 /* Output iface is not MPLS-enabled */ 128 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) { 129#ifdef MPLS_DEBUG 130 printf("MPLS_DEBUG: interface not mpls enabled\n"); 131#endif 132 error = ENETUNREACH; 133 goto bad; 134 } 135 136 /* reset broadcast and multicast flags, this is a P2P tunnel */ 137 m->m_flags &= ~(M_BCAST | M_MCAST); 138 139 smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK; 140 error = ifp->if_ll_output(ifp, m, smplstosa(smpls), rt); 141 return (error); 142bad: 143 m_freem(m); 144 return (error); 145} 146 147u_int8_t 148mpls_getttl(struct mbuf *m, sa_family_t af) 149{ 150 struct mbuf *n; 151 int loc, off; 152 u_int8_t ttl = mpls_defttl; 153 154 /* If the AF is MPLS then inherit the TTL from the present label. */ 155 if (af == AF_MPLS) 156 loc = 3; 157 else { 158 switch (*mtod(m, uint8_t *) >> 4) { 159 case 4: 160 if (!mpls_mapttl_ip) 161 return (ttl); 162 163 loc = offsetof(struct ip, ip_ttl); 164 break; 165#ifdef INET6 166 case 6: 167 if (!mpls_mapttl_ip6) 168 return (ttl); 169 170 loc = offsetof(struct ip6_hdr, ip6_hlim); 171 break; 172#endif 173 default: 174 return (ttl); 175 } 176 } 177 178 n = m_getptr(m, loc, &off); 179 if (n == NULL) 180 return (ttl); 181 182 ttl = *(mtod(n, uint8_t *) + off); 183 184 return (ttl); 185} 186